/
ipv6.h
280 lines (230 loc) · 7.68 KB
/
ipv6.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
/*
* Copyright (C) 2016 Authors of Cilium
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __LIB_IPV6__
#define __LIB_IPV6__
#include <linux/ipv6.h>
#include "dbg.h"
#define IPV6_FLOWINFO_MASK htonl(0x0FFFFFFF)
#define IPV6_FLOWLABEL_MASK htonl(0x000FFFFF)
#define IPV6_FLOWLABEL_STATELESS_FLAG htonl(0x00080000)
#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK)
#define IPV6_TCLASS_SHIFT 20
/* Number of extension headers that can be skipped */
#define IPV6_MAX_HEADERS 10
#define NEXTHDR_HOP 0 /* Hop-by-hop option header. */
#define NEXTHDR_TCP 6 /* TCP segment. */
#define NEXTHDR_UDP 17 /* UDP message. */
#define NEXTHDR_IPV6 41 /* IPv6 in IPv6 */
#define NEXTHDR_ROUTING 43 /* Routing header. */
#define NEXTHDR_FRAGMENT 44 /* Fragmentation/reassembly header. */
#define NEXTHDR_GRE 47 /* GRE header. */
#define NEXTHDR_ESP 50 /* Encapsulating security payload. */
#define NEXTHDR_AUTH 51 /* Authentication header. */
#define NEXTHDR_ICMP 58 /* ICMP for IPv6. */
#define NEXTHDR_NONE 59 /* No next header */
#define NEXTHDR_DEST 60 /* Destination options header. */
#define NEXTHDR_SCTP 132 /* SCTP message. */
#define NEXTHDR_MOBILITY 135 /* Mobility header. */
#define NEXTHDR_MAX 255
static inline int ipv6_optlen(struct ipv6_opt_hdr *opthdr)
{
return (opthdr->hdrlen + 1) << 3;
}
static inline int ipv6_authlen(struct ipv6_opt_hdr *opthdr)
{
return (opthdr->hdrlen + 2) << 2;
}
static inline int __inline__ ipv6_hdrlen(struct __sk_buff *skb, int l3_off, __u8 *nexthdr)
{
int i, len = sizeof(struct ipv6hdr);
struct ipv6_opt_hdr opthdr;
__u8 nh = *nexthdr;
#pragma unroll
for (i = 0; i < IPV6_MAX_HEADERS; i++) {
switch (nh) {
case NEXTHDR_NONE:
return DROP_INVALID_EXTHDR;
case NEXTHDR_FRAGMENT:
return DROP_FRAG_NOSUPPORT;
case NEXTHDR_HOP:
case NEXTHDR_ROUTING:
case NEXTHDR_AUTH:
case NEXTHDR_DEST:
if (skb_load_bytes(skb, l3_off + len, &opthdr, sizeof(opthdr)) < 0)
return DROP_INVALID;
nh = opthdr.nexthdr;
if (nh == NEXTHDR_AUTH)
len += ipv6_authlen(&opthdr);
else
len += ipv6_optlen(&opthdr);
break;
default:
*nexthdr = nh;
return len;
}
}
/* Reached limit of supported extension headers */
return DROP_INVALID_EXTHDR;
}
static inline void ipv6_addr_copy(union v6addr *dst, union v6addr *src)
{
dst->p1 = src->p1;
dst->p2 = src->p2;
dst->p3 = src->p3;
dst->p4 = src->p4;
}
static inline int ipv6_addrcmp(union v6addr *a, union v6addr *b)
{
int tmp;
tmp = a->p1 - b->p1;
if (!tmp) {
tmp = a->p2 - b->p2;
if (!tmp) {
tmp = a->p3 - b->p3;
if (!tmp)
tmp = a->p4 - b->p4;
}
}
return tmp;
}
static inline int ipv6_match_prefix_96(const union v6addr *addr, const union v6addr *prefix)
{
int tmp;
tmp = addr->p1 - prefix->p1;
if (!tmp) {
tmp = addr->p2 - prefix->p2;
if (!tmp)
tmp = addr->p3 - prefix->p3;
}
return !tmp;
}
static inline int ipv6_match_prefix_64(const union v6addr *addr, const union v6addr *prefix)
{
int tmp;
tmp = addr->p1 - prefix->p1;
if (!tmp)
tmp = addr->p2 - prefix->p2;
return !tmp;
}
static inline int ipv6_dec_hoplimit(struct __sk_buff *skb, int off)
{
__u8 hoplimit, new_hl;
hoplimit = load_byte(skb, off + offsetof(struct ipv6hdr, hop_limit));
if (hoplimit <= 1) {
return 1;
}
new_hl = hoplimit - 1;
if (skb_store_bytes(skb, off + offsetof(struct ipv6hdr, hop_limit),
&new_hl, sizeof(new_hl), BPF_F_RECOMPUTE_CSUM) < 0)
return DROP_WRITE_ERROR;
return 0;
}
static inline int ipv6_load_saddr(struct __sk_buff *skb, int off, union v6addr *dst)
{
return skb_load_bytes(skb, off + offsetof(struct ipv6hdr, saddr), dst->addr,
sizeof(((struct ipv6hdr *)NULL)->saddr));
}
/* Assumes that caller fixes checksum csum_diff() and l4_csum_replace() */
static inline int ipv6_store_saddr(struct __sk_buff *skb, __u8 *addr, int off)
{
return skb_store_bytes(skb, off + offsetof(struct ipv6hdr, saddr), addr, 16, 0);
}
static inline int ipv6_load_daddr(struct __sk_buff *skb, int off, union v6addr *dst)
{
return skb_load_bytes(skb, off + offsetof(struct ipv6hdr, daddr), dst->addr,
sizeof(((struct ipv6hdr *)NULL)->daddr));
}
/* Assumes that caller fixes checksum csum_diff() and l4_csum_replace() */
static inline int ipv6_store_daddr(struct __sk_buff *skb, __u8 *addr, int off)
{
return skb_store_bytes(skb, off + offsetof(struct ipv6hdr, daddr), addr, 16, 0);
}
static inline int ipv6_load_nexthdr(struct __sk_buff *skb, int off, __u8 *nexthdr)
{
return skb_load_bytes(skb, off + offsetof(struct ipv6hdr, nexthdr), nexthdr,
sizeof(__u8));
}
/* Assumes that caller fixes checksum csum_diff() and l4_csum_replace() */
static inline int ipv6_store_nexthdr(struct __sk_buff *skb, __u8 *nexthdr, int off)
{
return skb_store_bytes(skb, off + offsetof(struct ipv6hdr, nexthdr), nexthdr,
sizeof(__u8), 0);
}
static inline int ipv6_load_paylen(struct __sk_buff *skb, int off, __be16 *len)
{
return skb_load_bytes(skb, off + offsetof(struct ipv6hdr, payload_len),
len, sizeof(*len));
}
/* Assumes that caller fixes checksum csum_diff() and l4_csum_replace() */
static inline int ipv6_store_paylen(struct __sk_buff *skb, int off, __be16 *len)
{
return skb_store_bytes(skb, off + offsetof(struct ipv6hdr, payload_len),
len, sizeof(*len), 0);
}
static inline int ipv6_store_flowlabel(struct __sk_buff *skb, int off, __be32 label)
{
__u32 old;
/* use traffic class from packet */
if (skb_load_bytes(skb, off, &old, 4) < 0)
return DROP_INVALID;
old &= IPV6_TCLASS_MASK;
old = htonl(0x60000000) | label | old;
if (skb_store_bytes(skb, off, &old, 4, BPF_F_RECOMPUTE_CSUM) < 0)
return DROP_WRITE_ERROR;
return 0;
}
static inline __u16 derive_lxc_id(union v6addr *addr)
{
return ntohl(addr->p4) & 0xFFFF;
}
/* FIXME: rewrite this to avoid byte order conversion */
static inline void ipv6_set_lxc_id(union v6addr *addr, __u16 lxc_id)
{
__u32 p4 = ntohl(addr->p4);
p4 &= ~0xFFFF;
p4 |= lxc_id;
addr->p4 = htonl(p4);
}
static inline __u32 ipv6_derive_node_id(union v6addr *addr)
{
return ntohl(addr->p3);
}
static inline void ipv6_set_node_id(union v6addr *addr, __u32 node_id)
{
addr->p3 = htonl(node_id);
}
static inline __be32 ipv6_pseudohdr_checksum(struct ipv6hdr *hdr,
__u8 next_hdr,
__u16 payload_len, __be32 sum)
{
__u32 len = htonl((__u32)payload_len);
__u32 nexthdr = htonl((__u32)next_hdr);
sum = csum_diff(NULL, 0, &hdr->saddr, sizeof(struct in6_addr), sum);
sum = csum_diff(NULL, 0, &hdr->daddr, sizeof(struct in6_addr), sum);
sum = csum_diff(NULL, 0, &len, sizeof(len), sum);
sum = csum_diff(NULL, 0, &nexthdr, sizeof(nexthdr), sum);
return sum;
}
/*
* Ipv4 mapped address - 0:0:0:0:0:FFFF::/96
*/
static inline int ipv6_addr_is_mapped(union v6addr *addr)
{
return addr->p1 == 0 && addr->p2 == 0 && addr->p3 == 0xFFFF0000;
}
#endif /* __LIB_IPV6__ */