~diego-biurrun/hipl/hipl-s2e

« back to all changes in this revision

Viewing changes to hipfw/rewrite.c

  • Committer: Diego Biurrun
  • Date: 2013-03-26 12:51:51 UTC
  • mfrom: (6052.1.357 hipl)
  • Revision ID: diego@biurrun.de-20130326125151-19tea8nj10m70m8n
Merge current HIPL HEAD revision.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2011 Aalto University and RWTH Aachen University.
 
3
 *
 
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
 
11
 * conditions:
 
12
 *
 
13
 * The above copyright notice and this permission notice shall be
 
14
 * included in all copies or substantial portions of the Software.
 
15
 *
 
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.
 
24
 */
 
25
 
 
26
/**
 
27
 * @file
 
28
 *
 
29
 * Support and utility functions for packet rewriting, especially for growing
 
30
 * packets.
 
31
 *
 
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
 
34
 * bytes of payload.
 
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.
 
40
 *
 
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.
 
45
 *
 
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.
 
49
 *
 
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).
 
56
 *
 
57
 * @author Christof Mroz <christof.mroz@rwth-aachen.de>
 
58
 */
 
59
 
 
60
#define _BSD_SOURCE
 
61
 
 
62
#include <netinet/in.h>
 
63
#include <linux/netfilter.h>
 
64
#include <libipq.h>
 
65
#include <limits.h>
 
66
#include <stdlib.h>
 
67
#include <string.h>
 
68
 
 
69
#include "libcore/builder.h"
 
70
#include "libcore/debug.h"
 
71
#include "libcore/gpl/checksum.h"
 
72
#include "rewrite.h"
 
73
 
 
74
// static configuration
 
75
static const bool assume_ipq_buffer_sufficient = false;
 
76
 
 
77
struct scratch_buffer {
 
78
    ipq_packet_msg_t ipq;
 
79
    uint8_t         *payload[HIP_MAX_PACKET];
 
80
} __attribute__((packed)); // no gaps between header and payload
 
81
 
 
82
static struct scratch_buffer scratch_buffer;
 
83
 
 
84
/**
 
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.
 
87
 *
 
88
 * As an example use case, say we have a pointer into @a old:
 
89
 * <code>
 
90
 * char old[LEN];
 
91
 * ptr = &old[BLA];
 
92
 * </code>
 
93
 * Now after copying @a old to @a new, we need to update the pointer:
 
94
 * <code>
 
95
 * ptr = rebase(ptr, old, new);
 
96
 * </code>
 
97
 *
 
98
 * @note This is essentially equivalent to
 
99
 *       <code>
 
100
 *       return &new.field
 
101
 *       </code>
 
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.
 
105
 */
 
106
static void *rebase(const void *const field,
 
107
                    const void *const old,
 
108
                    void *const new)
 
109
{
 
110
    HIP_ASSERT((const char *) field >= (const char *) old);
 
111
    return (char *) new + ((const char *) field - (const char *) old);
 
112
}
 
113
 
 
114
/**
 
115
 * Mark packet as modified and indicate that the packet buffer passed by libipq
 
116
 * is overwritten with packet content.
 
117
 *
 
118
 * @param ctx The current packet context.
 
119
 *
 
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.
 
122
 */
 
123
static void hip_fw_context_enable_write_inplace(struct hip_fw_context *const ctx)
 
124
{
 
125
    ctx->modified = 1;
 
126
}
 
127
 
 
128
/**
 
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.
 
133
 *
 
134
 * @param ctx The current packet context. The ipq header may have been modified
 
135
 *            but should be consistent, especially the data_len field.
 
136
 *
 
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.
 
139
 */
 
140
static void hip_fw_context_enable_write(struct hip_fw_context *const ctx)
 
141
{
 
142
    HIP_ASSERT(ctx);
 
143
    HIP_ASSERT(ctx->ipq_packet);
 
144
 
 
145
    if (assume_ipq_buffer_sufficient) {
 
146
        hip_fw_context_enable_write_inplace(ctx);
 
147
        return;
 
148
    }
 
149
 
 
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);
 
155
        } else {
 
156
            HIP_ASSERT(ctx->ip_version == 6);
 
157
            ctx->ip_hdr.ipv6 = rebase(ctx->ip_hdr.ipv6, ctx->ipq_packet,
 
158
                                      &scratch_buffer.ipq);
 
159
        }
 
160
 
 
161
        switch (ctx->packet_type) {
 
162
        case ESP_PACKET:
 
163
            ctx->transport_hdr.esp = rebase(ctx->transport_hdr.esp, ctx->ipq_packet,
 
164
                                            &scratch_buffer.ipq);
 
165
            break;
 
166
        case HIP_PACKET:
 
167
            ctx->transport_hdr.hip = rebase(ctx->transport_hdr.hip, ctx->ipq_packet,
 
168
                                            &scratch_buffer.ipq);
 
169
            break;
 
170
        case OTHER_PACKET:
 
171
            break;
 
172
        default:
 
173
            HIP_ASSERT(false);
 
174
        }
 
175
 
 
176
        if (ctx->udp_encap_hdr) {
 
177
            ctx->udp_encap_hdr = rebase(ctx->udp_encap_hdr, ctx->ipq_packet,
 
178
                                        &scratch_buffer.ipq);
 
179
        }
 
180
 
 
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;
 
185
        ctx->modified   = 1;
 
186
    } else {
 
187
        // second invocation
 
188
        HIP_ASSERT(ctx->modified);
 
189
    }
 
190
}
 
191
 
 
192
/**
 
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.
 
196
 *
 
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.
 
200
 */
 
201
bool hipfw_splice_param(struct hip_fw_context *const ctx,
 
202
                        const struct hip_tlv_common *const param)
 
203
{
 
204
    HIP_ASSERT(ctx);
 
205
    HIP_ASSERT(ctx->packet_type == HIP_PACKET);
 
206
    HIP_ASSERT(param);
 
207
 
 
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
 
212
 
 
213
    // RFC 5201: Types 0 - 1023 are signed, so they must not be moved
 
214
    HIP_ASSERT(param_type >= 1024);
 
215
 
 
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);
 
219
        return false;
 
220
    }
 
221
 
 
222
    hip_fw_context_enable_write(ctx);
 
223
 
 
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;
 
227
 
 
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
 
232
 
 
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;
 
236
 
 
237
            memmove(splice + param_len, splice, end - splice);
 
238
            out = splice;
 
239
            break;
 
240
        }
 
241
    }
 
242
 
 
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);
 
246
    }
 
247
 
 
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
 
250
 
 
251
    return true;
 
252
}
 
253
 
 
254
/**
 
255
 * Getter for the position of the IP payload in an IPv4 packet. This allows to
 
256
 * handle IP options.
 
257
 *
 
258
 * @param ipv4 The IPv4 packet.
 
259
 */
 
260
inline static void *get_ipv4_payload(struct ip *const ipv4)
 
261
{
 
262
    return ((uint8_t *) ipv4) + 4 * ipv4->ip_hl;
 
263
}
 
264
 
 
265
/**
 
266
 * Update the UDP header after modifying the higher layer packet content.
 
267
 *
 
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).
 
272
 */
 
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)
 
277
{
 
278
    const uint16_t tot_len = sizeof(*udp) + payload_len;
 
279
 
 
280
    HIP_ASSERT(sizeof(*udp) == 8);
 
281
 
 
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);
 
285
}
 
286
 
 
287
/**
 
288
 * Update the IPv4 header after modifying the higher layer packet content.
 
289
 *
 
290
 * @param ipv4 The IPv4 packet.
 
291
 * @param payload_len The length of the IPv4 payload.
 
292
 */
 
293
static void update_ipv4_header(struct ip *const ipv4,
 
294
                               const size_t payload_len)
 
295
{
 
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);
 
299
}
 
300
 
 
301
/**
 
302
 * Update the IPv6 header after modifying the higher layer packet content.
 
303
 *
 
304
 * @param ipv6 The IPv6 packet.
 
305
 * @param payload_len The length of the IPv6 payload.
 
306
 */
 
307
static void update_ipv6_header(struct ip6_hdr *const ipv6,
 
308
                               const size_t payload_len)
 
309
{
 
310
    ipv6->ip6_plen = htons(payload_len);
 
311
}
 
312
 
 
313
/**
 
314
 * Set an accept verdict for a modified packet
 
315
 *
 
316
 * @param handle libipq file handle
 
317
 * @param ctx The current packet context.
 
318
 */
 
319
void allow_modified_packet(struct ipq_handle *const handle,
 
320
                           struct hip_fw_context *const ctx)
 
321
{
 
322
    HIP_ASSERT(ctx->modified);
 
323
 
 
324
    //
 
325
    // TODO: send as separate packets if fragmented?
 
326
    //
 
327
 
 
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);
 
331
 
 
332
        if (ctx->ip_version == 4) {
 
333
            struct ip *const ipv4 = ctx->ip_hdr.ipv4;
 
334
 
 
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;
 
338
 
 
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
 
343
            } else {
 
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 };
 
348
 
 
349
                HIP_ASSERT(ipv4->ip_p == IPPROTO_HIP);
 
350
 
 
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);
 
356
            }
 
357
        } else {
 
358
            HIP_ASSERT(ctx->ip_version == 6);
 
359
 
 
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 };
 
365
 
 
366
            HIP_ASSERT(ipv6->ip6_nxt == IPPROTO_HIP);
 
367
 
 
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);
 
373
        }
 
374
    }
 
375
 
 
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");
 
379
}