2
* Copyright (c) 2011 Aalto University and RWTH Aachen University.
4
* Permission is hereby granted, free of charge, to any person
5
* obtaining a copy of this software and associated documentation
6
* files (the "Software"), to deal in the Software without
7
* restriction, including without limitation the rights to use,
8
* copy, modify, merge, publish, distribute, sublicense, and/or sell
9
* copies of the Software, and to permit persons to whom the
10
* Software is furnished to do so, subject to the following
13
* The above copyright notice and this permission notice shall be
14
* included in all copies or substantial portions of the Software.
16
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
* OTHER DEALINGS IN THE SOFTWARE.
29
* Support and utility functions for packet rewriting, especially for growing
32
* Note that the buffer supplied to ipq_get_packet() in the firewall main loop
33
* is not big enough to hold both the ipq_packet header and ::HIP_MAX_PACKET
35
* Furthermore, the actual amount of writable data behind the received payload
36
* is not documented by the libipq team. This makes sense if you consider that
37
* netlink is able to push multiple packets into our userspace buffer for
38
* efficiency reasons. Unfortunately, it is not documented whether libipq
39
* actually makes use of this feature either.
41
* For this reason, all packet rewriting that needs access beyond the boundaries
42
* of the originally received packet should copy it into a big, temporary buffer
43
* first. This is managed by the hip_fw_context_enable_write() function; see its
44
* documentation for more info.
46
* After growing, the data_len field and the length of the innermost frame
47
* (currently either HIP oder ESP) must be updated: Checksums and outer length
48
* fields are updated only once, right before reinjecting the packet.
50
* @note Copying the packet incurs a considerable performance hit. Browsing the
51
* source code of libipq, netfilter_queue and netlink, one can find
52
* assumptions about the original buffer and its usage to save copying in
53
* some cases, but unless the internals of these interfaces are documented
54
* more thoroughly, these optimizations should be considered hacks (and
55
* are thus not used here).
57
* @author Christof Mroz <christof.mroz@rwth-aachen.de>
62
#include <netinet/in.h>
63
#include <linux/netfilter.h>
69
#include "libcore/builder.h"
70
#include "libcore/debug.h"
71
#include "libcore/gpl/checksum.h"
74
// static configuration
75
static const bool assume_ipq_buffer_sufficient = false;
77
struct scratch_buffer {
79
uint8_t *payload[HIP_MAX_PACKET];
80
} __attribute__((packed)); // no gaps between header and payload
82
static struct scratch_buffer scratch_buffer;
85
* Given the address @a field of a struct member of @a old, return
86
* the same field's address but offset from @a new.
88
* As an example use case, say we have a pointer into @a old:
93
* Now after copying @a old to @a new, we need to update the pointer:
95
* ptr = rebase(ptr, old, new);
98
* @note This is essentially equivalent to
102
* But of course, the above can only be used if we actually know
103
* the name of @a field, while this function needs just the
104
* address and base pointer.
106
static void *rebase(const void *const field,
107
const void *const old,
110
HIP_ASSERT((const char *) field >= (const char *) old);
111
return (char *) new + ((const char *) field - (const char *) old);
115
* Mark packet as modified and indicate that the packet buffer passed by libipq
116
* is overwritten with packet content.
118
* @param ctx The current packet context.
120
* @note Only set this flag when sure that the new packet will not exceed the
121
* buffer length of the packet originally passed by libipq.
123
static void hip_fw_context_enable_write_inplace(struct hip_fw_context *const ctx)
129
* Mark packet as modified and enable rewritten packet to grow up to
130
* ::HIP_MAX_PACKET bytes.
131
* The buffer will be available via the ipq_packet->payload member of @a ctx, as
132
* usual. That is: To the caller, it should look like nothing happened.
134
* @param ctx The current packet context. The ipq header may have been modified
135
* but should be consistent, especially the data_len field.
137
* @note It is safe to call this function on the same @a ctx multiple times: the
138
* packet will not needlessly be copied again.
140
static void hip_fw_context_enable_write(struct hip_fw_context *const ctx)
143
HIP_ASSERT(ctx->ipq_packet);
145
if (assume_ipq_buffer_sufficient) {
146
hip_fw_context_enable_write_inplace(ctx);
150
if (ctx->ipq_packet != &scratch_buffer.ipq) {
151
// simply rebase the old pointers
152
if (ctx->ip_version == 4) {
153
ctx->ip_hdr.ipv4 = rebase(ctx->ip_hdr.ipv4, ctx->ipq_packet,
154
&scratch_buffer.ipq);
156
HIP_ASSERT(ctx->ip_version == 6);
157
ctx->ip_hdr.ipv6 = rebase(ctx->ip_hdr.ipv6, ctx->ipq_packet,
158
&scratch_buffer.ipq);
161
switch (ctx->packet_type) {
163
ctx->transport_hdr.esp = rebase(ctx->transport_hdr.esp, ctx->ipq_packet,
164
&scratch_buffer.ipq);
167
ctx->transport_hdr.hip = rebase(ctx->transport_hdr.hip, ctx->ipq_packet,
168
&scratch_buffer.ipq);
176
if (ctx->udp_encap_hdr) {
177
ctx->udp_encap_hdr = rebase(ctx->udp_encap_hdr, ctx->ipq_packet,
178
&scratch_buffer.ipq);
181
// copy ipq packet plus payload
182
memcpy(&scratch_buffer.ipq, ctx->ipq_packet,
183
sizeof(*ctx->ipq_packet) + ctx->ipq_packet->data_len);
184
ctx->ipq_packet = &scratch_buffer.ipq;
188
HIP_ASSERT(ctx->modified);
193
* Add a new parameter to the correct position in the packet. Parameters are
194
* ordered by type number. Hence, some parameters might need to be moved in
195
* order for the new parameter to fit into the right position.
197
* @param ctx The current packet context.
198
* @param param The parameter to be added to the packet.
199
* @return true on success, false otherwise.
201
bool hipfw_splice_param(struct hip_fw_context *const ctx,
202
const struct hip_tlv_common *const param)
205
HIP_ASSERT(ctx->packet_type == HIP_PACKET);
208
const size_t hip_len = hip_get_msg_total_len(ctx->transport_hdr.hip); // padded
209
const size_t param_len = hip_get_param_total_len(param); // padded
210
const hip_tlv param_type = hip_get_param_type(param);
211
const size_t contents_len = hip_get_param_contents_len(param); // not padded
213
// RFC 5201: Types 0 - 1023 are signed, so they must not be moved
214
HIP_ASSERT(param_type >= 1024);
216
if (ctx->ipq_packet->data_len + param_len > sizeof(scratch_buffer.payload)) {
217
HIP_ERROR("New parameter of type %u, effective size %u, "
218
"does not fit into packet", param_type, param_len);
222
hip_fw_context_enable_write(ctx);
224
// note: this works because param_len is padded! otherwise, resize the hip
225
// packet and call hip_get_msg_total_len() instead.
226
ctx->ipq_packet->data_len += param_len;
228
struct hip_common *const hip = ctx->transport_hdr.hip;
229
uint8_t *const end = ((uint8_t *) hip) + hip_len;
230
struct hip_tlv_common *current = NULL;
231
uint8_t *out = end; // append by default
233
while ((current = hip_get_next_param_readwrite(hip, current))) {
234
if (hip_get_param_type(current) >= param_type) {
235
uint8_t *const splice = (uint8_t *const) current;
237
memmove(splice + param_len, splice, end - splice);
243
if ((sizeof(struct hip_tlv_common) + contents_len) != param_len) {
244
// padding needed: don't send uninitialized data
245
memset(out, 0, param_len);
248
memcpy(out, param, sizeof(struct hip_tlv_common) + contents_len);
249
hip_set_msg_total_len(hip, hip_len + param_len); // IP length etc. will be inferred
255
* Getter for the position of the IP payload in an IPv4 packet. This allows to
258
* @param ipv4 The IPv4 packet.
260
inline static void *get_ipv4_payload(struct ip *const ipv4)
262
return ((uint8_t *) ipv4) + 4 * ipv4->ip_hl;
266
* Update the UDP header after modifying the higher layer packet content.
268
* @param udp The UDP packet.
269
* @param payload_len The length of the UDP payload.
270
* @param src_ip The source IP address (needed for pseudo-header).
271
* @param dst_ip The destination IP address (needed for pseudo-header).
273
static void update_udp_header(struct udphdr *const udp,
274
const size_t payload_len,
275
const struct in_addr src_ip,
276
const struct in_addr dst_ip)
278
const uint16_t tot_len = sizeof(*udp) + payload_len;
280
HIP_ASSERT(sizeof(*udp) == 8);
282
udp->len = htons(tot_len);
283
udp->check = htons(0);
284
udp->check = ipv4_checksum(IPPROTO_UDP, &src_ip, &dst_ip, udp, tot_len);
288
* Update the IPv4 header after modifying the higher layer packet content.
290
* @param ipv4 The IPv4 packet.
291
* @param payload_len The length of the IPv4 payload.
293
static void update_ipv4_header(struct ip *const ipv4,
294
const size_t payload_len)
296
ipv4->ip_len = htons(ipv4->ip_hl * 4 + payload_len);
297
ipv4->ip_sum = htons(0);
298
ipv4->ip_sum = checksum_ip(ipv4, ipv4->ip_hl);
302
* Update the IPv6 header after modifying the higher layer packet content.
304
* @param ipv6 The IPv6 packet.
305
* @param payload_len The length of the IPv6 payload.
307
static void update_ipv6_header(struct ip6_hdr *const ipv6,
308
const size_t payload_len)
310
ipv6->ip6_plen = htons(payload_len);
314
* Set an accept verdict for a modified packet
316
* @param handle libipq file handle
317
* @param ctx The current packet context.
319
void allow_modified_packet(struct ipq_handle *const handle,
320
struct hip_fw_context *const ctx)
322
HIP_ASSERT(ctx->modified);
325
// TODO: send as separate packets if fragmented?
328
if (ctx->packet_type == HIP_PACKET) {
329
struct hip_common *const hip = ctx->transport_hdr.hip;
330
const size_t hip_len = hip_get_msg_total_len(hip);
332
if (ctx->ip_version == 4) {
333
struct ip *const ipv4 = ctx->ip_hdr.ipv4;
335
if (ipv4->ip_p == IPPROTO_UDP) {
336
// UDP Payload: "zero SPI" (0x00000000) + HIP
337
const size_t udp_len = HIP_UDP_ZERO_BYTES_LEN + hip_len;
339
update_udp_header(get_ipv4_payload(ipv4), udp_len,
340
ipv4->ip_src, ipv4->ip_dst);
341
update_ipv4_header(ipv4, sizeof(struct udphdr) + udp_len);
342
// HIP checksum unused
344
const struct sockaddr_in src = { .sin_family = AF_INET,
345
.sin_addr = ipv4->ip_src };
346
const struct sockaddr_in dst = { .sin_family = AF_INET,
347
.sin_addr = ipv4->ip_dst };
349
HIP_ASSERT(ipv4->ip_p == IPPROTO_HIP);
351
hip_zero_msg_checksum(hip);
352
hip->checksum = hip_checksum_packet((char *) hip,
353
(const struct sockaddr *) &src,
354
(const struct sockaddr *) &dst);
355
update_ipv4_header(ipv4, hip_len);
358
HIP_ASSERT(ctx->ip_version == 6);
360
struct ip6_hdr *const ipv6 = ctx->ip_hdr.ipv6;
361
const struct sockaddr_in6 src = { .sin6_family = AF_INET6,
362
.sin6_addr = ipv6->ip6_src };
363
const struct sockaddr_in6 dst = { .sin6_family = AF_INET6,
364
.sin6_addr = ipv6->ip6_dst };
366
HIP_ASSERT(ipv6->ip6_nxt == IPPROTO_HIP);
368
hip_zero_msg_checksum(hip);
369
hip->checksum = hip_checksum_packet((char *) hip,
370
(const struct sockaddr *) &src,
371
(const struct sockaddr *) &dst);
372
update_ipv6_header(ipv6, hip_len);
376
ipq_set_verdict(handle, ctx->ipq_packet->packet_id, NF_ACCEPT,
377
ctx->ipq_packet->data_len, ctx->ipq_packet->payload);
378
HIP_DEBUG("Packet accepted with modifications\n\n");