OpenWrt – Blame information for rev 3
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | From: Steven Barth <steven@midlink.org> |
2 | Subject: Add support for MAP-E FMRs (mesh mode) |
||
3 | |||
4 | MAP-E FMRs (draft-ietf-softwire-map-10) are rules for IPv4-communication |
||
5 | between MAP CEs (mesh mode) without the need to forward such data to a |
||
6 | border relay. This is similar to how 6rd works but for IPv4 over IPv6. |
||
7 | |||
8 | Signed-off-by: Steven Barth <cyrus@openwrt.org> |
||
9 | --- |
||
10 | include/net/ip6_tunnel.h | 13 ++ |
||
11 | include/uapi/linux/if_tunnel.h | 13 ++ |
||
12 | net/ipv6/ip6_tunnel.c | 276 +++++++++++++++++++++++++++++++++++++++-- |
||
13 | 3 files changed, 291 insertions(+), 11 deletions(-) |
||
14 | |||
15 | --- a/include/net/ip6_tunnel.h |
||
16 | +++ b/include/net/ip6_tunnel.h |
||
17 | @@ -17,6 +17,18 @@ |
||
18 | /* determine capability on a per-packet basis */ |
||
19 | #define IP6_TNL_F_CAP_PER_PACKET 0x40000 |
||
20 | |||
21 | +/* IPv6 tunnel FMR */ |
||
22 | +struct __ip6_tnl_fmr { |
||
23 | + struct __ip6_tnl_fmr *next; /* next fmr in list */ |
||
24 | + struct in6_addr ip6_prefix; |
||
25 | + struct in_addr ip4_prefix; |
||
26 | + |
||
27 | + __u8 ip6_prefix_len; |
||
28 | + __u8 ip4_prefix_len; |
||
29 | + __u8 ea_len; |
||
30 | + __u8 offset; |
||
31 | +}; |
||
32 | + |
||
33 | struct __ip6_tnl_parm { |
||
34 | char name[IFNAMSIZ]; /* name of tunnel device */ |
||
35 | int link; /* ifindex of underlying L2 interface */ |
||
36 | @@ -28,6 +40,7 @@ struct __ip6_tnl_parm { |
||
37 | __u32 flags; /* tunnel flags */ |
||
38 | struct in6_addr laddr; /* local tunnel end-point address */ |
||
39 | struct in6_addr raddr; /* remote tunnel end-point address */ |
||
40 | + struct __ip6_tnl_fmr *fmrs; /* FMRs */ |
||
41 | |||
42 | __be16 i_flags; |
||
43 | __be16 o_flags; |
||
44 | --- a/include/uapi/linux/if_tunnel.h |
||
45 | +++ b/include/uapi/linux/if_tunnel.h |
||
46 | @@ -75,10 +75,23 @@ enum { |
||
47 | IFLA_IPTUN_ENCAP_SPORT, |
||
48 | IFLA_IPTUN_ENCAP_DPORT, |
||
49 | IFLA_IPTUN_COLLECT_METADATA, |
||
50 | + IFLA_IPTUN_FMRS, |
||
51 | __IFLA_IPTUN_MAX, |
||
52 | }; |
||
53 | #define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) |
||
54 | |||
55 | +enum { |
||
56 | + IFLA_IPTUN_FMR_UNSPEC, |
||
57 | + IFLA_IPTUN_FMR_IP6_PREFIX, |
||
58 | + IFLA_IPTUN_FMR_IP4_PREFIX, |
||
59 | + IFLA_IPTUN_FMR_IP6_PREFIX_LEN, |
||
60 | + IFLA_IPTUN_FMR_IP4_PREFIX_LEN, |
||
61 | + IFLA_IPTUN_FMR_EA_LEN, |
||
62 | + IFLA_IPTUN_FMR_OFFSET, |
||
63 | + __IFLA_IPTUN_FMR_MAX, |
||
64 | +}; |
||
65 | +#define IFLA_IPTUN_FMR_MAX (__IFLA_IPTUN_FMR_MAX - 1) |
||
66 | + |
||
67 | enum tunnel_encap_types { |
||
68 | TUNNEL_ENCAP_NONE, |
||
69 | TUNNEL_ENCAP_FOU, |
||
70 | --- a/net/ipv6/ip6_tunnel.c |
||
71 | +++ b/net/ipv6/ip6_tunnel.c |
||
72 | @@ -16,6 +16,8 @@ |
||
73 | * as published by the Free Software Foundation; either version |
||
74 | * 2 of the License, or (at your option) any later version. |
||
75 | * |
||
76 | + * Changes: |
||
77 | + * Steven Barth <cyrus@openwrt.org>: MAP-E FMR support |
||
78 | */ |
||
79 | |||
80 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
||
81 | @@ -72,9 +74,9 @@ static bool log_ecn_error = true; |
||
82 | module_param(log_ecn_error, bool, 0644); |
||
83 | MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); |
||
84 | |||
85 | -static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2) |
||
86 | +static u32 HASH(const struct in6_addr *addr) |
||
87 | { |
||
88 | - u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2); |
||
89 | + u32 hash = ipv6_addr_hash(addr); |
||
90 | |||
91 | return hash_32(hash, IP6_TUNNEL_HASH_SIZE_SHIFT); |
||
92 | } |
||
93 | @@ -141,20 +143,29 @@ static struct net_device_stats *ip6_get_ |
||
94 | static struct ip6_tnl * |
||
95 | ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_addr *local) |
||
96 | { |
||
97 | - unsigned int hash = HASH(remote, local); |
||
98 | + unsigned int hash = HASH(local); |
||
99 | struct ip6_tnl *t; |
||
100 | struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); |
||
101 | struct in6_addr any; |
||
102 | + struct __ip6_tnl_fmr *fmr; |
||
103 | |||
104 | for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { |
||
105 | - if (ipv6_addr_equal(local, &t->parms.laddr) && |
||
106 | - ipv6_addr_equal(remote, &t->parms.raddr) && |
||
107 | - (t->dev->flags & IFF_UP)) |
||
108 | + if (!ipv6_addr_equal(local, &t->parms.laddr) || |
||
109 | + !(t->dev->flags & IFF_UP)) |
||
110 | + continue; |
||
111 | + |
||
112 | + if (ipv6_addr_equal(remote, &t->parms.raddr)) |
||
113 | return t; |
||
114 | + |
||
115 | + for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) { |
||
116 | + if (ipv6_prefix_equal(remote, &fmr->ip6_prefix, |
||
117 | + fmr->ip6_prefix_len)) |
||
118 | + return t; |
||
119 | + } |
||
120 | } |
||
121 | |||
122 | memset(&any, 0, sizeof(any)); |
||
123 | - hash = HASH(&any, local); |
||
124 | + hash = HASH(local); |
||
125 | for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { |
||
126 | if (ipv6_addr_equal(local, &t->parms.laddr) && |
||
127 | ipv6_addr_any(&t->parms.raddr) && |
||
128 | @@ -162,7 +173,7 @@ ip6_tnl_lookup(struct net *net, const st |
||
129 | return t; |
||
130 | } |
||
131 | |||
132 | - hash = HASH(remote, &any); |
||
133 | + hash = HASH(&any); |
||
134 | for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) { |
||
135 | if (ipv6_addr_equal(remote, &t->parms.raddr) && |
||
136 | ipv6_addr_any(&t->parms.laddr) && |
||
137 | @@ -202,7 +213,7 @@ ip6_tnl_bucket(struct ip6_tnl_net *ip6n, |
||
138 | |||
139 | if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { |
||
140 | prio = 1; |
||
141 | - h = HASH(remote, local); |
||
142 | + h = HASH(local); |
||
143 | } |
||
144 | return &ip6n->tnls[prio][h]; |
||
145 | } |
||
146 | @@ -384,6 +395,12 @@ ip6_tnl_dev_uninit(struct net_device *de |
||
147 | struct net *net = t->net; |
||
148 | struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); |
||
149 | |||
150 | + while (t->parms.fmrs) { |
||
151 | + struct __ip6_tnl_fmr *next = t->parms.fmrs->next; |
||
152 | + kfree(t->parms.fmrs); |
||
153 | + t->parms.fmrs = next; |
||
154 | + } |
||
155 | + |
||
156 | if (dev == ip6n->fb_tnl_dev) |
||
157 | RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL); |
||
158 | else |
||
159 | @@ -780,6 +797,107 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t, |
||
160 | } |
||
161 | EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl); |
||
162 | |||
163 | +/** |
||
164 | + * ip4ip6_fmr_calc - calculate target / source IPv6-address based on FMR |
||
165 | + * @dest: destination IPv6 address buffer |
||
166 | + * @skb: received socket buffer |
||
167 | + * @fmr: MAP FMR |
||
168 | + * @xmit: Calculate for xmit or rcv |
||
169 | + **/ |
||
170 | +static void ip4ip6_fmr_calc(struct in6_addr *dest, |
||
171 | + const struct iphdr *iph, const uint8_t *end, |
||
172 | + const struct __ip6_tnl_fmr *fmr, bool xmit) |
||
173 | +{ |
||
174 | + int psidlen = fmr->ea_len - (32 - fmr->ip4_prefix_len); |
||
175 | + u8 *portp = NULL; |
||
176 | + bool use_dest_addr; |
||
177 | + const struct iphdr *dsth = iph; |
||
178 | + |
||
179 | + if ((u8*)dsth >= end) |
||
180 | + return; |
||
181 | + |
||
182 | + /* find significant IP header */ |
||
183 | + if (iph->protocol == IPPROTO_ICMP) { |
||
184 | + struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4); |
||
185 | + if (ih && ((u8*)&ih[1]) <= end && ( |
||
186 | + ih->type == ICMP_DEST_UNREACH || |
||
187 | + ih->type == ICMP_SOURCE_QUENCH || |
||
188 | + ih->type == ICMP_TIME_EXCEEDED || |
||
189 | + ih->type == ICMP_PARAMETERPROB || |
||
190 | + ih->type == ICMP_REDIRECT)) |
||
191 | + dsth = (const struct iphdr*)&ih[1]; |
||
192 | + } |
||
193 | + |
||
194 | + /* in xmit-path use dest port by default and source port only if |
||
195 | + this is an ICMP reply to something else; vice versa in rcv-path */ |
||
196 | + use_dest_addr = (xmit && dsth == iph) || (!xmit && dsth != iph); |
||
197 | + |
||
198 | + /* get dst port */ |
||
199 | + if (((u8*)&dsth[1]) <= end && ( |
||
200 | + dsth->protocol == IPPROTO_UDP || |
||
201 | + dsth->protocol == IPPROTO_TCP || |
||
202 | + dsth->protocol == IPPROTO_SCTP || |
||
203 | + dsth->protocol == IPPROTO_DCCP)) { |
||
204 | + /* for UDP, TCP, SCTP and DCCP source and dest port |
||
205 | + follow IPv4 header directly */ |
||
206 | + portp = ((u8*)dsth) + dsth->ihl * 4; |
||
207 | + |
||
208 | + if (use_dest_addr) |
||
209 | + portp += sizeof(u16); |
||
210 | + } else if (iph->protocol == IPPROTO_ICMP) { |
||
211 | + struct icmphdr *ih = (struct icmphdr*)(((u8*)dsth) + dsth->ihl * 4); |
||
212 | + |
||
213 | + /* use icmp identifier as port */ |
||
214 | + if (((u8*)&ih) <= end && ( |
||
215 | + (use_dest_addr && ( |
||
216 | + ih->type == ICMP_ECHOREPLY || |
||
217 | + ih->type == ICMP_TIMESTAMPREPLY || |
||
218 | + ih->type == ICMP_INFO_REPLY || |
||
219 | + ih->type == ICMP_ADDRESSREPLY)) || |
||
220 | + (!use_dest_addr && ( |
||
221 | + ih->type == ICMP_ECHO || |
||
222 | + ih->type == ICMP_TIMESTAMP || |
||
223 | + ih->type == ICMP_INFO_REQUEST || |
||
224 | + ih->type == ICMP_ADDRESS) |
||
225 | + ))) |
||
226 | + portp = (u8*)&ih->un.echo.id; |
||
227 | + } |
||
228 | + |
||
229 | + if ((portp && &portp[2] <= end) || psidlen == 0) { |
||
230 | + int frombyte = fmr->ip6_prefix_len / 8; |
||
231 | + int fromrem = fmr->ip6_prefix_len % 8; |
||
232 | + int bytes = sizeof(struct in6_addr) - frombyte; |
||
233 | + const u32 *addr = (use_dest_addr) ? &iph->daddr : &iph->saddr; |
||
234 | + u64 eabits = ((u64)ntohl(*addr)) << (32 + fmr->ip4_prefix_len); |
||
235 | + u64 t = 0; |
||
236 | + |
||
237 | + /* extract PSID from port and add it to eabits */ |
||
238 | + u16 psidbits = 0; |
||
239 | + if (psidlen > 0) { |
||
240 | + psidbits = ((u16)portp[0]) << 8 | ((u16)portp[1]); |
||
241 | + psidbits >>= 16 - psidlen - fmr->offset; |
||
242 | + psidbits = (u16)(psidbits << (16 - psidlen)); |
||
243 | + eabits |= ((u64)psidbits) << (48 - (fmr->ea_len - psidlen)); |
||
244 | + } |
||
245 | + |
||
246 | + /* rewrite destination address */ |
||
247 | + *dest = fmr->ip6_prefix; |
||
248 | + memcpy(&dest->s6_addr[10], addr, sizeof(*addr)); |
||
249 | + dest->s6_addr16[7] = htons(psidbits >> (16 - psidlen)); |
||
250 | + |
||
251 | + if (bytes > sizeof(u64)) |
||
252 | + bytes = sizeof(u64); |
||
253 | + |
||
254 | + /* insert eabits */ |
||
255 | + memcpy(&t, &dest->s6_addr[frombyte], bytes); |
||
256 | + t = be64_to_cpu(t) & ~(((((u64)1) << fmr->ea_len) - 1) |
||
257 | + << (64 - fmr->ea_len - fromrem)); |
||
258 | + t = cpu_to_be64(t | (eabits >> fromrem)); |
||
259 | + memcpy(&dest->s6_addr[frombyte], &t, bytes); |
||
260 | + } |
||
261 | +} |
||
262 | + |
||
263 | + |
||
264 | static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb, |
||
265 | const struct tnl_ptk_info *tpi, |
||
266 | struct metadata_dst *tun_dst, |
||
267 | @@ -832,6 +950,27 @@ static int __ip6_tnl_rcv(struct ip6_tnl |
||
268 | skb_reset_network_header(skb); |
||
269 | memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); |
||
270 | |||
271 | + if (tpi->proto == htons(ETH_P_IP) && tunnel->parms.fmrs && |
||
272 | + !ipv6_addr_equal(&ipv6h->saddr, &tunnel->parms.raddr)) { |
||
273 | + /* Packet didn't come from BR, so lookup FMR */ |
||
274 | + struct __ip6_tnl_fmr *fmr; |
||
275 | + struct in6_addr expected = tunnel->parms.raddr; |
||
276 | + for (fmr = tunnel->parms.fmrs; fmr; fmr = fmr->next) |
||
277 | + if (ipv6_prefix_equal(&ipv6h->saddr, |
||
278 | + &fmr->ip6_prefix, fmr->ip6_prefix_len)) |
||
279 | + break; |
||
280 | + |
||
281 | + /* Check that IPv6 matches IPv4 source to prevent spoofing */ |
||
282 | + if (fmr) |
||
283 | + ip4ip6_fmr_calc(&expected, ip_hdr(skb), |
||
284 | + skb_tail_pointer(skb), fmr, false); |
||
285 | + |
||
286 | + if (!ipv6_addr_equal(&ipv6h->saddr, &expected)) { |
||
287 | + rcu_read_unlock(); |
||
288 | + goto drop; |
||
289 | + } |
||
290 | + } |
||
291 | + |
||
292 | __skb_tunnel_rx(skb, tunnel->dev, tunnel->net); |
||
293 | |||
294 | err = dscp_ecn_decapsulate(tunnel, ipv6h, skb); |
||
3 | office | 295 | @@ -961,6 +1100,7 @@ static void init_tel_txopt(struct ipv6_t |
1 | office | 296 | opt->ops.opt_nflen = 8; |
297 | } |
||
298 | |||
299 | + |
||
300 | /** |
||
301 | * ip6_tnl_addr_conflict - compare packet addresses to tunnel's own |
||
302 | * @t: the outgoing tunnel device |
||
3 | office | 303 | @@ -1298,6 +1438,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, str |
1 | office | 304 | { |
305 | struct ip6_tnl *t = netdev_priv(dev); |
||
3 | office | 306 | struct ipv6hdr *ipv6h = ipv6_hdr(skb); |
1 | office | 307 | + struct __ip6_tnl_fmr *fmr; |
308 | int encap_limit = -1; |
||
309 | __u16 offset; |
||
310 | struct flowi6 fl6; |
||
3 | office | 311 | @@ -1356,6 +1497,18 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, str |
1 | office | 312 | fl6.flowi6_mark = skb->mark; |
313 | } |
||
314 | |||
315 | + /* try to find matching FMR */ |
||
316 | + for (fmr = t->parms.fmrs; fmr; fmr = fmr->next) { |
||
317 | + unsigned mshift = 32 - fmr->ip4_prefix_len; |
||
318 | + if (ntohl(fmr->ip4_prefix.s_addr) >> mshift == |
||
319 | + ntohl(ip_hdr(skb)->daddr) >> mshift) |
||
320 | + break; |
||
321 | + } |
||
322 | + |
||
323 | + /* change dstaddr according to FMR */ |
||
324 | + if (fmr) |
||
325 | + ip4ip6_fmr_calc(&fl6.daddr, ip_hdr(skb), skb_tail_pointer(skb), fmr, true); |
||
326 | + |
||
327 | if (iptunnel_handle_offloads(skb, SKB_GSO_IPXIP6)) |
||
328 | return -1; |
||
329 | |||
3 | office | 330 | @@ -1483,6 +1636,14 @@ ip6_tnl_change(struct ip6_tnl *t, const |
1 | office | 331 | t->parms.flowinfo = p->flowinfo; |
332 | t->parms.link = p->link; |
||
333 | t->parms.proto = p->proto; |
||
334 | + |
||
335 | + while (t->parms.fmrs) { |
||
336 | + struct __ip6_tnl_fmr *next = t->parms.fmrs->next; |
||
337 | + kfree(t->parms.fmrs); |
||
338 | + t->parms.fmrs = next; |
||
339 | + } |
||
340 | + t->parms.fmrs = p->fmrs; |
||
341 | + |
||
342 | dst_cache_reset(&t->dst_cache); |
||
343 | ip6_tnl_link_config(t); |
||
344 | return 0; |
||
3 | office | 345 | @@ -1521,6 +1682,7 @@ ip6_tnl_parm_from_user(struct __ip6_tnl_ |
1 | office | 346 | p->flowinfo = u->flowinfo; |
347 | p->link = u->link; |
||
348 | p->proto = u->proto; |
||
349 | + p->fmrs = NULL; |
||
350 | memcpy(p->name, u->name, sizeof(u->name)); |
||
351 | } |
||
352 | |||
3 | office | 353 | @@ -1898,6 +2060,15 @@ static int ip6_tnl_validate(struct nlatt |
1 | office | 354 | return 0; |
355 | } |
||
356 | |||
357 | +static const struct nla_policy ip6_tnl_fmr_policy[IFLA_IPTUN_FMR_MAX + 1] = { |
||
358 | + [IFLA_IPTUN_FMR_IP6_PREFIX] = { .len = sizeof(struct in6_addr) }, |
||
359 | + [IFLA_IPTUN_FMR_IP4_PREFIX] = { .len = sizeof(struct in_addr) }, |
||
360 | + [IFLA_IPTUN_FMR_IP6_PREFIX_LEN] = { .type = NLA_U8 }, |
||
361 | + [IFLA_IPTUN_FMR_IP4_PREFIX_LEN] = { .type = NLA_U8 }, |
||
362 | + [IFLA_IPTUN_FMR_EA_LEN] = { .type = NLA_U8 }, |
||
363 | + [IFLA_IPTUN_FMR_OFFSET] = { .type = NLA_U8 } |
||
364 | +}; |
||
365 | + |
||
366 | static void ip6_tnl_netlink_parms(struct nlattr *data[], |
||
367 | struct __ip6_tnl_parm *parms) |
||
368 | { |
||
3 | office | 369 | @@ -1932,6 +2103,46 @@ static void ip6_tnl_netlink_parms(struct |
1 | office | 370 | |
371 | if (data[IFLA_IPTUN_COLLECT_METADATA]) |
||
372 | parms->collect_md = true; |
||
373 | + |
||
374 | + if (data[IFLA_IPTUN_FMRS]) { |
||
375 | + unsigned rem; |
||
376 | + struct nlattr *fmr; |
||
377 | + nla_for_each_nested(fmr, data[IFLA_IPTUN_FMRS], rem) { |
||
378 | + struct nlattr *fmrd[IFLA_IPTUN_FMR_MAX + 1], *c; |
||
379 | + struct __ip6_tnl_fmr *nfmr; |
||
380 | + |
||
381 | + nla_parse_nested(fmrd, IFLA_IPTUN_FMR_MAX, |
||
382 | + fmr, ip6_tnl_fmr_policy); |
||
383 | + |
||
384 | + if (!(nfmr = kzalloc(sizeof(*nfmr), GFP_KERNEL))) |
||
385 | + continue; |
||
386 | + |
||
387 | + nfmr->offset = 6; |
||
388 | + |
||
389 | + if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX])) |
||
390 | + nla_memcpy(&nfmr->ip6_prefix, fmrd[IFLA_IPTUN_FMR_IP6_PREFIX], |
||
391 | + sizeof(nfmr->ip6_prefix)); |
||
392 | + |
||
393 | + if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX])) |
||
394 | + nla_memcpy(&nfmr->ip4_prefix, fmrd[IFLA_IPTUN_FMR_IP4_PREFIX], |
||
395 | + sizeof(nfmr->ip4_prefix)); |
||
396 | + |
||
397 | + if ((c = fmrd[IFLA_IPTUN_FMR_IP6_PREFIX_LEN])) |
||
398 | + nfmr->ip6_prefix_len = nla_get_u8(c); |
||
399 | + |
||
400 | + if ((c = fmrd[IFLA_IPTUN_FMR_IP4_PREFIX_LEN])) |
||
401 | + nfmr->ip4_prefix_len = nla_get_u8(c); |
||
402 | + |
||
403 | + if ((c = fmrd[IFLA_IPTUN_FMR_EA_LEN])) |
||
404 | + nfmr->ea_len = nla_get_u8(c); |
||
405 | + |
||
406 | + if ((c = fmrd[IFLA_IPTUN_FMR_OFFSET])) |
||
407 | + nfmr->offset = nla_get_u8(c); |
||
408 | + |
||
409 | + nfmr->next = parms->fmrs; |
||
410 | + parms->fmrs = nfmr; |
||
411 | + } |
||
412 | + } |
||
413 | } |
||
414 | |||
415 | static bool ip6_tnl_netlink_encap_parms(struct nlattr *data[], |
||
3 | office | 416 | @@ -2045,6 +2256,12 @@ static void ip6_tnl_dellink(struct net_d |
1 | office | 417 | |
418 | static size_t ip6_tnl_get_size(const struct net_device *dev) |
||
419 | { |
||
420 | + const struct ip6_tnl *t = netdev_priv(dev); |
||
421 | + struct __ip6_tnl_fmr *c; |
||
422 | + int fmrs = 0; |
||
423 | + for (c = t->parms.fmrs; c; c = c->next) |
||
424 | + ++fmrs; |
||
425 | + |
||
426 | return |
||
427 | /* IFLA_IPTUN_LINK */ |
||
428 | nla_total_size(4) + |
||
3 | office | 429 | @@ -2072,6 +2289,24 @@ static size_t ip6_tnl_get_size(const str |
1 | office | 430 | nla_total_size(2) + |
431 | /* IFLA_IPTUN_COLLECT_METADATA */ |
||
432 | nla_total_size(0) + |
||
433 | + /* IFLA_IPTUN_FMRS */ |
||
434 | + nla_total_size(0) + |
||
435 | + ( |
||
436 | + /* nest */ |
||
437 | + nla_total_size(0) + |
||
438 | + /* IFLA_IPTUN_FMR_IP6_PREFIX */ |
||
439 | + nla_total_size(sizeof(struct in6_addr)) + |
||
440 | + /* IFLA_IPTUN_FMR_IP4_PREFIX */ |
||
441 | + nla_total_size(sizeof(struct in_addr)) + |
||
442 | + /* IFLA_IPTUN_FMR_EA_LEN */ |
||
443 | + nla_total_size(1) + |
||
444 | + /* IFLA_IPTUN_FMR_IP6_PREFIX_LEN */ |
||
445 | + nla_total_size(1) + |
||
446 | + /* IFLA_IPTUN_FMR_IP4_PREFIX_LEN */ |
||
447 | + nla_total_size(1) + |
||
448 | + /* IFLA_IPTUN_FMR_OFFSET */ |
||
449 | + nla_total_size(1) |
||
450 | + ) * fmrs + |
||
451 | 0; |
||
452 | } |
||
453 | |||
3 | office | 454 | @@ -2079,6 +2314,9 @@ static int ip6_tnl_fill_info(struct sk_b |
1 | office | 455 | { |
456 | struct ip6_tnl *tunnel = netdev_priv(dev); |
||
457 | struct __ip6_tnl_parm *parm = &tunnel->parms; |
||
458 | + struct __ip6_tnl_fmr *c; |
||
459 | + int fmrcnt = 0; |
||
460 | + struct nlattr *fmrs; |
||
461 | |||
462 | if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) || |
||
463 | nla_put_in6_addr(skb, IFLA_IPTUN_LOCAL, &parm->laddr) || |
||
3 | office | 464 | @@ -2087,9 +2325,27 @@ static int ip6_tnl_fill_info(struct sk_b |
1 | office | 465 | nla_put_u8(skb, IFLA_IPTUN_ENCAP_LIMIT, parm->encap_limit) || |
466 | nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) || |
||
467 | nla_put_u32(skb, IFLA_IPTUN_FLAGS, parm->flags) || |
||
468 | - nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto)) |
||
469 | + nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto) || |
||
470 | + !(fmrs = nla_nest_start(skb, IFLA_IPTUN_FMRS))) |
||
471 | goto nla_put_failure; |
||
472 | |||
473 | + for (c = parm->fmrs; c; c = c->next) { |
||
474 | + struct nlattr *fmr = nla_nest_start(skb, ++fmrcnt); |
||
475 | + if (!fmr || |
||
476 | + nla_put(skb, IFLA_IPTUN_FMR_IP6_PREFIX, |
||
477 | + sizeof(c->ip6_prefix), &c->ip6_prefix) || |
||
478 | + nla_put(skb, IFLA_IPTUN_FMR_IP4_PREFIX, |
||
479 | + sizeof(c->ip4_prefix), &c->ip4_prefix) || |
||
480 | + nla_put_u8(skb, IFLA_IPTUN_FMR_IP6_PREFIX_LEN, c->ip6_prefix_len) || |
||
481 | + nla_put_u8(skb, IFLA_IPTUN_FMR_IP4_PREFIX_LEN, c->ip4_prefix_len) || |
||
482 | + nla_put_u8(skb, IFLA_IPTUN_FMR_EA_LEN, c->ea_len) || |
||
483 | + nla_put_u8(skb, IFLA_IPTUN_FMR_OFFSET, c->offset)) |
||
484 | + goto nla_put_failure; |
||
485 | + |
||
486 | + nla_nest_end(skb, fmr); |
||
487 | + } |
||
488 | + nla_nest_end(skb, fmrs); |
||
489 | + |
||
490 | if (nla_put_u16(skb, IFLA_IPTUN_ENCAP_TYPE, tunnel->encap.type) || |
||
491 | nla_put_be16(skb, IFLA_IPTUN_ENCAP_SPORT, tunnel->encap.sport) || |
||
492 | nla_put_be16(skb, IFLA_IPTUN_ENCAP_DPORT, tunnel->encap.dport) || |
||
3 | office | 493 | @@ -2127,6 +2383,7 @@ static const struct nla_policy ip6_tnl_p |
1 | office | 494 | [IFLA_IPTUN_ENCAP_SPORT] = { .type = NLA_U16 }, |
495 | [IFLA_IPTUN_ENCAP_DPORT] = { .type = NLA_U16 }, |
||
496 | [IFLA_IPTUN_COLLECT_METADATA] = { .type = NLA_FLAG }, |
||
497 | + [IFLA_IPTUN_FMRS] = { .type = NLA_NESTED }, |
||
498 | }; |
||
499 | |||
500 | static struct rtnl_link_ops ip6_link_ops __read_mostly = { |