Blame view

net/ipv6/exthdrs.c 20.6 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
  /*
   *	Extension Header handling for IPv6
   *	Linux INET6 implementation
   *
   *	Authors:
   *	Pedro Roque		<roque@di.fc.ul.pt>
   *	Andi Kleen		<ak@muc.de>
   *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
15
16
   *	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.
   */
  
  /* Changes:
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
17
   *	yoshfuji		: ensure not to overrun while parsing
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
22
23
24
25
26
27
   *				  tlv options.
   *	Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs().
   *	YOSHIFUJI Hideaki @USAGI  Register inbound extension header
   *				  handlers as inet6_protocol{}.
   */
  
  #include <linux/errno.h>
  #include <linux/types.h>
  #include <linux/socket.h>
  #include <linux/sockios.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
31
  #include <linux/net.h>
  #include <linux/netdevice.h>
  #include <linux/in6.h>
  #include <linux/icmpv6.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
32
  #include <linux/slab.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
33
  #include <linux/export.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34

352e512c3   Herbert Xu   [NET]: Eliminate ...
35
  #include <net/dst.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
38
39
40
41
42
43
44
45
  #include <net/sock.h>
  #include <net/snmp.h>
  
  #include <net/ipv6.h>
  #include <net/protocol.h>
  #include <net/transp_v6.h>
  #include <net/rawv6.h>
  #include <net/ndisc.h>
  #include <net/ip6_route.h>
  #include <net/addrconf.h>
07a936260   Amerigo Wang   ipv6: use IS_ENAB...
46
  #if IS_ENABLED(CONFIG_IPV6_MIP6)
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
47
48
  #include <net/xfrm.h>
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
51
52
53
54
  
  #include <asm/uaccess.h>
  
  /*
   *	Parsing tlv encoded headers.
   *
a50feda54   Eric Dumazet   ipv6: bool/const ...
55
56
   *	Parsing function "func" returns true, if parsing succeed
   *	and false, if it failed.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
59
60
61
   *	It MUST NOT touch skb->h.
   */
  
  struct tlvtype_proc {
  	int	type;
a50feda54   Eric Dumazet   ipv6: bool/const ...
62
  	bool	(*func)(struct sk_buff *skb, int offset);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
66
67
68
69
  };
  
  /*********************
    Generic functions
   *********************/
  
  /* An unknown option is detected, decide what to do */
a50feda54   Eric Dumazet   ipv6: bool/const ...
70
  static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
  {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
72
  	switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  	case 0: /* ignore */
a50feda54   Eric Dumazet   ipv6: bool/const ...
74
  		return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
76
77
78
79
80
81
82
  
  	case 1: /* drop packet */
  		break;
  
  	case 3: /* Send ICMP if not a multicast address and drop packet */
  		/* Actually, it is redundant check. icmp_send
  		   will recheck in any case.
  		 */
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
83
  		if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
86
  			break;
  	case 2: /* send ICMP PARM PROB regardless and drop packet */
  		icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff);
a50feda54   Eric Dumazet   ipv6: bool/const ...
87
  		return false;
3ff50b799   Stephen Hemminger   [NET]: cleanup ex...
88
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
  
  	kfree_skb(skb);
a50feda54   Eric Dumazet   ipv6: bool/const ...
91
  	return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
  }
  
  /* Parse tlv encoded option header (hop-by-hop or destination) */
a50feda54   Eric Dumazet   ipv6: bool/const ...
95
  static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
  {
a50feda54   Eric Dumazet   ipv6: bool/const ...
97
  	const struct tlvtype_proc *curr;
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
98
  	const unsigned char *nh = skb_network_header(skb);
cfe1fc775   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
99
  	int off = skb_network_header_len(skb);
9c70220b7   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
100
  	int len = (skb_transport_header(skb)[1] + 1) << 3;
9b905fe68   Eldad Zack   ipv6/exthdrs: str...
101
  	int padlen = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102

ea2ae17d6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
103
  	if (skb_transport_offset(skb) + len > skb_headlen(skb))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
106
107
108
109
  		goto bad;
  
  	off += 2;
  	len -= 2;
  
  	while (len > 0) {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
110
  		int optlen = nh[off + 1] + 2;
c1412fce7   Eldad Zack   net/ipv6/exthdrs....
111
  		int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112

d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
113
  		switch (nh[off]) {
1de5a71c3   Eldad Zack   ipv6: correct the...
114
  		case IPV6_TLV_PAD1:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
  			optlen = 1;
9b905fe68   Eldad Zack   ipv6/exthdrs: str...
116
117
118
  			padlen++;
  			if (padlen > 7)
  				goto bad;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119
120
121
  			break;
  
  		case IPV6_TLV_PADN:
c1412fce7   Eldad Zack   net/ipv6/exthdrs....
122
123
124
125
126
  			/* RFC 2460 states that the purpose of PadN is
  			 * to align the containing header to multiples
  			 * of 8. 7 is therefore the highest valid value.
  			 * See also RFC 4942, Section 2.1.9.5.
  			 */
9b905fe68   Eldad Zack   ipv6/exthdrs: str...
127
128
  			padlen += optlen;
  			if (padlen > 7)
c1412fce7   Eldad Zack   net/ipv6/exthdrs....
129
130
131
132
133
134
135
136
137
  				goto bad;
  			/* RFC 4942 recommends receiving hosts to
  			 * actively check PadN payload to contain
  			 * only zeroes.
  			 */
  			for (i = 2; i < optlen; i++) {
  				if (nh[off + i] != 0)
  					goto bad;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
141
142
143
  			break;
  
  		default: /* Other TLV code so scan list */
  			if (optlen > len)
  				goto bad;
  			for (curr=procs; curr->type >= 0; curr++) {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
144
  				if (curr->type == nh[off]) {
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
145
146
  					/* type specific length/alignment
  					   checks will be performed in the
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
  					   func(). */
a50feda54   Eric Dumazet   ipv6: bool/const ...
148
149
  					if (curr->func(skb, off) == false)
  						return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
152
153
  					break;
  				}
  			}
  			if (curr->type < 0) {
e5bbef20e   Herbert Xu   [IPV6]: Replace s...
154
  				if (ip6_tlvopt_unknown(skb, off) == 0)
a50feda54   Eric Dumazet   ipv6: bool/const ...
155
  					return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
  			}
9b905fe68   Eldad Zack   ipv6/exthdrs: str...
157
  			padlen = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
160
161
162
  			break;
  		}
  		off += optlen;
  		len -= optlen;
  	}
9b905fe68   Eldad Zack   ipv6/exthdrs: str...
163

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
  	if (len == 0)
a50feda54   Eric Dumazet   ipv6: bool/const ...
165
  		return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
  bad:
  	kfree_skb(skb);
a50feda54   Eric Dumazet   ipv6: bool/const ...
168
  	return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
171
172
173
  }
  
  /*****************************
    Destination options header.
   *****************************/
07a936260   Amerigo Wang   ipv6: use IS_ENAB...
174
  #if IS_ENABLED(CONFIG_IPV6_MIP6)
a50feda54   Eric Dumazet   ipv6: bool/const ...
175
  static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
176
  {
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
177
178
  	struct ipv6_destopt_hao *hao;
  	struct inet6_skb_parm *opt = IP6CB(skb);
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
179
  	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
180
181
182
183
184
185
186
187
188
189
  	struct in6_addr tmp_addr;
  	int ret;
  
  	if (opt->dsthao) {
  		LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated
  ");
  		goto discard;
  	}
  	opt->dsthao = opt->dst1;
  	opt->dst1 = 0;
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
190
  	hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
191
192
193
194
195
196
197
198
199
200
  
  	if (hao->length != 16) {
  		LIMIT_NETDEBUG(
  			KERN_DEBUG "hao invalid option length = %d
  ", hao->length);
  		goto discard;
  	}
  
  	if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
  		LIMIT_NETDEBUG(
5b095d989   Harvey Harrison   net: replace %p6 ...
201
202
  			KERN_DEBUG "hao is not an unicast addr: %pI6
  ", &hao->addr);
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
203
204
205
206
207
208
209
210
211
  		goto discard;
  	}
  
  	ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
  			       (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
  	if (unlikely(ret < 0))
  		goto discard;
  
  	if (skb_cloned(skb)) {
65c884666   Herbert Xu   [IPV6]: Avoid skb...
212
  		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
213
  			goto discard;
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
214
  		/* update all variable using below by copied skbuff */
65c884666   Herbert Xu   [IPV6]: Avoid skb...
215
  		hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) +
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
216
  						  optoff);
65c884666   Herbert Xu   [IPV6]: Avoid skb...
217
  		ipv6h = ipv6_hdr(skb);
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
218
219
220
221
  	}
  
  	if (skb->ip_summed == CHECKSUM_COMPLETE)
  		skb->ip_summed = CHECKSUM_NONE;
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
222
223
224
  	tmp_addr = ipv6h->saddr;
  	ipv6h->saddr = hao->addr;
  	hao->addr = tmp_addr;
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
225

b7aa0bf70   Eric Dumazet   [NET]: convert ne...
226
  	if (skb->tstamp.tv64 == 0)
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
227
  		__net_timestamp(skb);
a50feda54   Eric Dumazet   ipv6: bool/const ...
228
  	return true;
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
229
230
231
  
   discard:
  	kfree_skb(skb);
a50feda54   Eric Dumazet   ipv6: bool/const ...
232
  	return false;
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
233
234
  }
  #endif
a50feda54   Eric Dumazet   ipv6: bool/const ...
235
  static const struct tlvtype_proc tlvprocdestopt_lst[] = {
07a936260   Amerigo Wang   ipv6: use IS_ENAB...
236
  #if IS_ENABLED(CONFIG_IPV6_MIP6)
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
237
238
239
240
241
  	{
  		.type	= IPV6_TLV_HAO,
  		.func	= ipv6_dest_hao,
  	},
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
  	{-1,			NULL}
  };
e5bbef20e   Herbert Xu   [IPV6]: Replace s...
244
  static int ipv6_destopt_rcv(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
  	struct inet6_skb_parm *opt = IP6CB(skb);
07a936260   Amerigo Wang   ipv6: use IS_ENAB...
247
  #if IS_ENABLED(CONFIG_IPV6_MIP6)
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
248
249
  	__u16 dstbuf;
  #endif
897dc80b9   Eric Dumazet   ipv6: avoid a dst...
250
  	struct dst_entry *dst = skb_dst(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251

ea2ae17d6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
252
253
  	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
  	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
9c70220b7   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
254
  				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
897dc80b9   Eric Dumazet   ipv6: avoid a dst...
255
  		IP6_INC_STATS_BH(dev_net(dst->dev), ip6_dst_idev(dst),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
256
  				 IPSTATS_MIB_INHDRERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257
258
259
  		kfree_skb(skb);
  		return -1;
  	}
cfe1fc775   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
260
  	opt->lastopt = opt->dst1 = skb_network_header_len(skb);
07a936260   Amerigo Wang   ipv6: use IS_ENAB...
261
  #if IS_ENABLED(CONFIG_IPV6_MIP6)
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
262
263
  	dstbuf = opt->dst1;
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264

e5bbef20e   Herbert Xu   [IPV6]: Replace s...
265
  	if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
266
  		skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
dc435e6da   Masahide NAKAMURA   [IPV6] MIP6: Fix ...
267
  		opt = IP6CB(skb);
07a936260   Amerigo Wang   ipv6: use IS_ENAB...
268
  #if IS_ENABLED(CONFIG_IPV6_MIP6)
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
269
270
  		opt->nhoff = dstbuf;
  #else
951dbc8ac   Patrick McHardy   [IPV6]: Move next...
271
  		opt->nhoff = opt->dst1;
a831f5bbc   Masahide NAKAMURA   [IPV6] MIP6: Add ...
272
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
  		return 1;
  	}
483a47d2f   Denis V. Lunev   ipv6: added net a...
275
276
  	IP6_INC_STATS_BH(dev_net(dst->dev),
  			 ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
  	return -1;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
280
281
  /********************************
    Routing header.
   ********************************/
f6bc7d9e4   Eric Dumazet   ipv6: avoid two a...
282
  /* called with rcu_read_lock() */
e5bbef20e   Herbert Xu   [IPV6]: Replace s...
283
  static int ipv6_rthdr_rcv(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
  	struct inet6_skb_parm *opt = IP6CB(skb);
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
286
  	struct in6_addr *addr = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
  	struct in6_addr daddr;
0bcbc9262   YOSHIFUJI Hideaki   [IPV6]: Disallow ...
288
  	struct inet6_dev *idev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289
  	int n, i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
  	struct ipv6_rt_hdr *hdr;
  	struct rt0_hdr *rthdr;
483a47d2f   Denis V. Lunev   ipv6: added net a...
292
293
  	struct net *net = dev_net(skb->dev);
  	int accept_source_route = net->ipv6.devconf_all->accept_source_route;
0bcbc9262   YOSHIFUJI Hideaki   [IPV6]: Disallow ...
294

f6bc7d9e4   Eric Dumazet   ipv6: avoid two a...
295
296
297
  	idev = __in6_dev_get(skb->dev);
  	if (idev && accept_source_route > idev->cnf.accept_source_route)
  		accept_source_route = idev->cnf.accept_source_route;
0bcbc9262   YOSHIFUJI Hideaki   [IPV6]: Disallow ...
298

ea2ae17d6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
299
300
  	if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) ||
  	    !pskb_may_pull(skb, (skb_transport_offset(skb) +
9c70220b7   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
301
  				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
adf30907d   Eric Dumazet   net: skb->dst acc...
302
  		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
303
  				 IPSTATS_MIB_INHDRERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
305
306
  		kfree_skb(skb);
  		return -1;
  	}
9c70220b7   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
307
  	hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308

0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
309
  	if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
  	    skb->pkt_type != PACKET_HOST) {
adf30907d   Eric Dumazet   net: skb->dst acc...
311
  		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
312
  				 IPSTATS_MIB_INADDRERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
315
316
317
318
  		kfree_skb(skb);
  		return -1;
  	}
  
  looped_back:
  	if (hdr->segments_left == 0) {
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
319
  		switch (hdr->type) {
07a936260   Amerigo Wang   ipv6: use IS_ENAB...
320
  #if IS_ENABLED(CONFIG_IPV6_MIP6)
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
321
322
323
324
325
  		case IPV6_SRCRT_TYPE_2:
  			/* Silently discard type 2 header unless it was
  			 * processed by own
  			 */
  			if (!addr) {
adf30907d   Eric Dumazet   net: skb->dst acc...
326
  				IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
327
  						 IPSTATS_MIB_INADDRERRORS);
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
328
329
330
331
332
333
334
335
  				kfree_skb(skb);
  				return -1;
  			}
  			break;
  #endif
  		default:
  			break;
  		}
cfe1fc775   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
336
  		opt->lastopt = opt->srcrt = skb_network_header_len(skb);
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
337
  		skb->transport_header += (hdr->hdrlen + 1) << 3;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
  		opt->dst0 = opt->dst1;
  		opt->dst1 = 0;
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
340
  		opt->nhoff = (&hdr->nexthdr) - skb_network_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
  		return 1;
  	}
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
343
  	switch (hdr->type) {
07a936260   Amerigo Wang   ipv6: use IS_ENAB...
344
  #if IS_ENABLED(CONFIG_IPV6_MIP6)
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
345
  	case IPV6_SRCRT_TYPE_2:
c382bb9d3   YOSHIFUJI Hideaki   [IPV6]: Restore s...
346
347
  		if (accept_source_route < 0)
  			goto unknown_rh;
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
348
349
  		/* Silently discard invalid RTH type 2 */
  		if (hdr->hdrlen != 2 || hdr->segments_left != 1) {
adf30907d   Eric Dumazet   net: skb->dst acc...
350
  			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
351
  					 IPSTATS_MIB_INHDRERRORS);
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
352
353
354
355
356
  			kfree_skb(skb);
  			return -1;
  		}
  		break;
  #endif
c382bb9d3   YOSHIFUJI Hideaki   [IPV6]: Restore s...
357
358
  	default:
  		goto unknown_rh;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
362
363
364
365
366
367
368
  
  	/*
  	 *	This is the routing header forwarding algorithm from
  	 *	RFC 2460, page 16.
  	 */
  
  	n = hdr->hdrlen >> 1;
  
  	if (hdr->segments_left > n) {
adf30907d   Eric Dumazet   net: skb->dst acc...
369
  		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
370
  				 IPSTATS_MIB_INHDRERRORS);
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
371
372
373
  		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
  				  ((&hdr->segments_left) -
  				   skb_network_header(skb)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374
375
376
377
378
379
380
  		return -1;
  	}
  
  	/* We are about to mangle packet header. Be careful!
  	   Do not damage packets queued somewhere.
  	 */
  	if (skb_cloned(skb)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381
  		/* the copy is a forwarded packet */
65c884666   Herbert Xu   [IPV6]: Avoid skb...
382
  		if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) {
adf30907d   Eric Dumazet   net: skb->dst acc...
383
  			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
384
385
  					 IPSTATS_MIB_OUTDISCARDS);
  			kfree_skb(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
387
  			return -1;
  		}
65c884666   Herbert Xu   [IPV6]: Avoid skb...
388
  		hdr = (struct ipv6_rt_hdr *)skb_transport_header(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
  	}
84fa7933a   Patrick McHardy   [NET]: Replace CH...
390
  	if (skb->ip_summed == CHECKSUM_COMPLETE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
393
394
395
396
397
  		skb->ip_summed = CHECKSUM_NONE;
  
  	i = n - --hdr->segments_left;
  
  	rthdr = (struct rt0_hdr *) hdr;
  	addr = rthdr->addr;
  	addr += i - 1;
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
398
  	switch (hdr->type) {
07a936260   Amerigo Wang   ipv6: use IS_ENAB...
399
  #if IS_ENABLED(CONFIG_IPV6_MIP6)
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
400
401
  	case IPV6_SRCRT_TYPE_2:
  		if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
402
  				     (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
403
  				     IPPROTO_ROUTING) < 0) {
adf30907d   Eric Dumazet   net: skb->dst acc...
404
  			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
405
  					 IPSTATS_MIB_INADDRERRORS);
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
406
407
408
  			kfree_skb(skb);
  			return -1;
  		}
adf30907d   Eric Dumazet   net: skb->dst acc...
409
410
  		if (!ipv6_chk_home_addr(dev_net(skb_dst(skb)->dev), addr)) {
  			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
411
  					 IPSTATS_MIB_INADDRERRORS);
65d4ed922   Masahide NAKAMURA   [IPV6] MIP6: Add ...
412
413
414
415
416
417
418
419
  			kfree_skb(skb);
  			return -1;
  		}
  		break;
  #endif
  	default:
  		break;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  	if (ipv6_addr_is_multicast(addr)) {
adf30907d   Eric Dumazet   net: skb->dst acc...
421
  		IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
422
  				 IPSTATS_MIB_INADDRERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
423
424
425
  		kfree_skb(skb);
  		return -1;
  	}
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
426
427
428
  	daddr = *addr;
  	*addr = ipv6_hdr(skb)->daddr;
  	ipv6_hdr(skb)->daddr = daddr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429

adf30907d   Eric Dumazet   net: skb->dst acc...
430
  	skb_dst_drop(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
  	ip6_route_input(skb);
adf30907d   Eric Dumazet   net: skb->dst acc...
432
  	if (skb_dst(skb)->error) {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
433
  		skb_push(skb, skb->data - skb_network_header(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
435
436
  		dst_input(skb);
  		return -1;
  	}
adf30907d   Eric Dumazet   net: skb->dst acc...
437
  	if (skb_dst(skb)->dev->flags&IFF_LOOPBACK) {
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
438
  		if (ipv6_hdr(skb)->hop_limit <= 1) {
adf30907d   Eric Dumazet   net: skb->dst acc...
439
  			IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
440
  					 IPSTATS_MIB_INHDRERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
  			icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
3ffe533c8   Alexey Dobriyan   ipv6: drop unused...
442
  				    0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
443
444
445
  			kfree_skb(skb);
  			return -1;
  		}
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
446
  		ipv6_hdr(skb)->hop_limit--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
447
448
  		goto looped_back;
  	}
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
449
  	skb_push(skb, skb->data - skb_network_header(skb));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
450
451
  	dst_input(skb);
  	return -1;
c382bb9d3   YOSHIFUJI Hideaki   [IPV6]: Restore s...
452
453
  
  unknown_rh:
adf30907d   Eric Dumazet   net: skb->dst acc...
454
  	IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_INHDRERRORS);
c382bb9d3   YOSHIFUJI Hideaki   [IPV6]: Restore s...
455
456
457
  	icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
  			  (&hdr->type) - skb_network_header(skb));
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
  }
41135cc83   Alexey Dobriyan   net: constify str...
459
  static const struct inet6_protocol rthdr_protocol = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
  	.handler	=	ipv6_rthdr_rcv,
2207afc8b   Vlad Yasevich   ipv6: Move exthdr...
461
  	.flags		=	INET6_PROTO_NOPOLICY,
8ca896cfd   Vlad Yasevich   ipv6: Add new off...
462
  };
41135cc83   Alexey Dobriyan   net: constify str...
463
  static const struct inet6_protocol destopt_protocol = {
248b238dc   Daniel Lezcano   [IPV6]: make exte...
464
  	.handler	=	ipv6_destopt_rcv,
2207afc8b   Vlad Yasevich   ipv6: Move exthdr...
465
  	.flags		=	INET6_PROTO_NOPOLICY,
8ca896cfd   Vlad Yasevich   ipv6: Add new off...
466
  };
41135cc83   Alexey Dobriyan   net: constify str...
467
  static const struct inet6_protocol nodata_protocol = {
248b238dc   Daniel Lezcano   [IPV6]: make exte...
468
469
470
471
472
  	.handler	=	dst_discard,
  	.flags		=	INET6_PROTO_NOPOLICY,
  };
  
  int __init ipv6_exthdrs_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
  {
248b238dc   Daniel Lezcano   [IPV6]: make exte...
474
  	int ret;
3336288a9   Vlad Yasevich   ipv6: Switch to u...
475
476
  	ret = inet6_add_protocol(&rthdr_protocol, IPPROTO_ROUTING);
  	if (ret)
c6b641a4c   Vlad Yasevich   ipv6: Pull IPv6 G...
477
  		goto out;
3336288a9   Vlad Yasevich   ipv6: Switch to u...
478

248b238dc   Daniel Lezcano   [IPV6]: make exte...
479
480
481
482
483
484
485
486
487
488
  	ret = inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
  	if (ret)
  		goto out_rthdr;
  
  	ret = inet6_add_protocol(&nodata_protocol, IPPROTO_NONE);
  	if (ret)
  		goto out_destopt;
  
  out:
  	return ret;
248b238dc   Daniel Lezcano   [IPV6]: make exte...
489
490
  out_destopt:
  	inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
3336288a9   Vlad Yasevich   ipv6: Switch to u...
491
492
  out_rthdr:
  	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
248b238dc   Daniel Lezcano   [IPV6]: make exte...
493
  	goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494
  };
248b238dc   Daniel Lezcano   [IPV6]: make exte...
495
496
497
498
499
500
  void ipv6_exthdrs_exit(void)
  {
  	inet6_del_protocol(&nodata_protocol, IPPROTO_NONE);
  	inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
  	inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
502
503
  /**********************************
    Hop-by-hop options.
   **********************************/
e76b2b256   YOSHIFUJI Hideaki   [IPV6]: Do no rel...
504
  /*
adf30907d   Eric Dumazet   net: skb->dst acc...
505
   * Note: we cannot rely on skb_dst(skb) before we assign it in ip6_route_input().
e76b2b256   YOSHIFUJI Hideaki   [IPV6]: Do no rel...
506
507
508
   */
  static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb)
  {
adf30907d   Eric Dumazet   net: skb->dst acc...
509
  	return skb_dst(skb) ? ip6_dst_idev(skb_dst(skb)) : __in6_dev_get(skb->dev);
e76b2b256   YOSHIFUJI Hideaki   [IPV6]: Do no rel...
510
  }
2570a4f54   David S. Miller   ipv6: skb_dst() c...
511
512
513
514
  static inline struct net *ipv6_skb_net(struct sk_buff *skb)
  {
  	return skb_dst(skb) ? dev_net(skb_dst(skb)->dev) : dev_net(skb->dev);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
  /* Router Alert as of RFC 2711 */
a50feda54   Eric Dumazet   ipv6: bool/const ...
516
  static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
517
  {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
518
  	const unsigned char *nh = skb_network_header(skb);
a80ff03e0   Masahide NAKAMURA   [IPV6]: Allow to ...
519

d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
520
  	if (nh[optoff + 1] == 2) {
dd3332bfc   YOSHIFUJI Hideaki / 吉藤英明   ipv6: Store Route...
521
522
  		IP6CB(skb)->flags |= IP6SKB_ROUTERALERT;
  		memcpy(&IP6CB(skb)->ra, nh + optoff + 2, sizeof(IP6CB(skb)->ra));
a50feda54   Eric Dumazet   ipv6: bool/const ...
523
  		return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
  	}
64ce20730   Patrick McHardy   [NET]: Make NETDE...
525
526
  	LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d
  ",
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
527
  		       nh[optoff + 1]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
  	kfree_skb(skb);
a50feda54   Eric Dumazet   ipv6: bool/const ...
529
  	return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
531
532
  }
  
  /* Jumbo payload */
a50feda54   Eric Dumazet   ipv6: bool/const ...
533
  static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
  {
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
535
  	const unsigned char *nh = skb_network_header(skb);
2570a4f54   David S. Miller   ipv6: skb_dst() c...
536
  	struct net *net = ipv6_skb_net(skb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
  	u32 pkt_len;
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
538
  	if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
64ce20730   Patrick McHardy   [NET]: Make NETDE...
539
540
  		LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d
  ",
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
541
  			       nh[optoff+1]);
483a47d2f   Denis V. Lunev   ipv6: added net a...
542
  		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
a11d206d0   YOSHIFUJI Hideaki   [IPV6]: Per-inter...
543
  				 IPSTATS_MIB_INHDRERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
545
  		goto drop;
  	}
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
546
  	pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
547
  	if (pkt_len <= IPV6_MAXPLEN) {
483a47d2f   Denis V. Lunev   ipv6: added net a...
548
549
  		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
  				 IPSTATS_MIB_INHDRERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
550
  		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
a50feda54   Eric Dumazet   ipv6: bool/const ...
551
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
  	}
0660e03f6   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
553
  	if (ipv6_hdr(skb)->payload_len) {
483a47d2f   Denis V. Lunev   ipv6: added net a...
554
555
  		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
  				 IPSTATS_MIB_INHDRERRORS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
  		icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
a50feda54   Eric Dumazet   ipv6: bool/const ...
557
  		return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
558
559
560
  	}
  
  	if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
483a47d2f   Denis V. Lunev   ipv6: added net a...
561
562
  		IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
  				 IPSTATS_MIB_INTRUNCATEDPKTS);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
563
564
  		goto drop;
  	}
42ca89c18   Stephen Hemminger   [IPV6]: Need to u...
565
566
567
  
  	if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
  		goto drop;
a50feda54   Eric Dumazet   ipv6: bool/const ...
568
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
569
570
571
  
  drop:
  	kfree_skb(skb);
a50feda54   Eric Dumazet   ipv6: bool/const ...
572
  	return false;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
  }
a50feda54   Eric Dumazet   ipv6: bool/const ...
574
  static const struct tlvtype_proc tlvprochopopt_lst[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
576
577
578
579
580
581
582
583
584
  	{
  		.type	= IPV6_TLV_ROUTERALERT,
  		.func	= ipv6_hop_ra,
  	},
  	{
  		.type	= IPV6_TLV_JUMBO,
  		.func	= ipv6_hop_jumbo,
  	},
  	{ -1, }
  };
e5bbef20e   Herbert Xu   [IPV6]: Replace s...
585
  int ipv6_parse_hopopts(struct sk_buff *skb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
586
  {
951dbc8ac   Patrick McHardy   [IPV6]: Move next...
587
  	struct inet6_skb_parm *opt = IP6CB(skb);
ec6700958   YOSHIFUJI Hideaki   [IPV6]: Ensure to...
588
  	/*
d56f90a7c   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
589
  	 * skb_network_header(skb) is equal to skb->data, and
cfe1fc775   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
590
  	 * skb_network_header_len(skb) is always equal to
ec6700958   YOSHIFUJI Hideaki   [IPV6]: Ensure to...
591
592
593
594
  	 * sizeof(struct ipv6hdr) by definition of
  	 * hop-by-hop options.
  	 */
  	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) ||
9c70220b7   Arnaldo Carvalho de Melo   [SK_BUFF]: Introd...
595
596
  	    !pskb_may_pull(skb, (sizeof(struct ipv6hdr) +
  				 ((skb_transport_header(skb)[1] + 1) << 3)))) {
ec6700958   YOSHIFUJI Hideaki   [IPV6]: Ensure to...
597
598
599
  		kfree_skb(skb);
  		return -1;
  	}
951dbc8ac   Patrick McHardy   [IPV6]: Move next...
600
  	opt->hop = sizeof(struct ipv6hdr);
e5bbef20e   Herbert Xu   [IPV6]: Replace s...
601
  	if (ip6_parse_tlv(tlvprochopopt_lst, skb)) {
b0e380b1d   Arnaldo Carvalho de Melo   [SK_BUFF]: unions...
602
  		skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
dc435e6da   Masahide NAKAMURA   [IPV6] MIP6: Fix ...
603
  		opt = IP6CB(skb);
951dbc8ac   Patrick McHardy   [IPV6]: Move next...
604
  		opt->nhoff = sizeof(struct ipv6hdr);
b809739a1   YOSHIFUJI Hideaki   [IPV6]: Clean up ...
605
  		return 1;
951dbc8ac   Patrick McHardy   [IPV6]: Move next...
606
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
  	return -1;
  }
  
  /*
   *	Creating outbound headers.
   *
   *	"build" functions work when skb is filled from head to tail (datagram)
   *	"push"	functions work when headers are added from tail to head (tcp)
   *
   *	In both cases we assume, that caller reserved enough room
   *	for headers.
   */
  
  static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
  			    struct ipv6_rt_hdr *opt,
  			    struct in6_addr **addr_p)
  {
  	struct rt0_hdr *phdr, *ihdr;
  	int hops;
  
  	ihdr = (struct rt0_hdr *) opt;
1ab1457c4   YOSHIFUJI Hideaki   [NET] IPV6: Fix w...
628

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
629
630
631
632
633
634
635
636
  	phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
  	memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
  
  	hops = ihdr->rt_hdr.hdrlen >> 1;
  
  	if (hops > 1)
  		memcpy(phdr->addr, ihdr->addr + 1,
  		       (hops - 1) * sizeof(struct in6_addr));
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
637
  	phdr->addr[hops - 1] = **addr_p;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
  	*addr_p = ihdr->addr;
  
  	phdr->rt_hdr.nexthdr = *proto;
  	*proto = NEXTHDR_ROUTING;
  }
  
  static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
  {
  	struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
  
  	memcpy(h, opt, ipv6_optlen(opt));
  	h->nexthdr = *proto;
  	*proto = type;
  }
  
  void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
  			  u8 *proto,
  			  struct in6_addr **daddr)
  {
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
657
  	if (opt->srcrt) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
  		ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
659
660
661
662
663
664
665
  		/*
  		 * IPV6_RTHDRDSTOPTS is ignored
  		 * unless IPV6_RTHDR is set (RFC3542).
  		 */
  		if (opt->dst0opt)
  			ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
667
668
  	if (opt->hopopt)
  		ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
  }
7159039a1   YOSHIFUJI Hideaki   [IPV6]: Decentral...
669
  EXPORT_SYMBOL(ipv6_push_nfrag_opts);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670
671
672
673
674
675
676
677
678
679
680
681
682
  void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
  {
  	if (opt->dst1opt)
  		ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
  }
  
  struct ipv6_txoptions *
  ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt)
  {
  	struct ipv6_txoptions *opt2;
  
  	opt2 = sock_kmalloc(sk, opt->tot_len, GFP_ATOMIC);
  	if (opt2) {
ac3c8172f   Eldad Zack   net/ipv6/exthdrs....
683
  		long dif = (char *)opt2 - (char *)opt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
684
685
  		memcpy(opt2, opt, opt->tot_len);
  		if (opt2->hopopt)
ac3c8172f   Eldad Zack   net/ipv6/exthdrs....
686
  			*((char **)&opt2->hopopt) += dif;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687
  		if (opt2->dst0opt)
ac3c8172f   Eldad Zack   net/ipv6/exthdrs....
688
  			*((char **)&opt2->dst0opt) += dif;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
689
  		if (opt2->dst1opt)
ac3c8172f   Eldad Zack   net/ipv6/exthdrs....
690
  			*((char **)&opt2->dst1opt) += dif;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
  		if (opt2->srcrt)
ac3c8172f   Eldad Zack   net/ipv6/exthdrs....
692
  			*((char **)&opt2->srcrt) += dif;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
693
694
695
  	}
  	return opt2;
  }
3cf3dc6c2   Arnaldo Carvalho de Melo   [IPV6]: Export so...
696
  EXPORT_SYMBOL_GPL(ipv6_dup_options);
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
697
698
699
700
701
702
703
704
705
706
  static int ipv6_renew_option(void *ohdr,
  			     struct ipv6_opt_hdr __user *newopt, int newoptlen,
  			     int inherit,
  			     struct ipv6_opt_hdr **hdr,
  			     char **p)
  {
  	if (inherit) {
  		if (ohdr) {
  			memcpy(*p, ohdr, ipv6_optlen((struct ipv6_opt_hdr *)ohdr));
  			*hdr = (struct ipv6_opt_hdr *)*p;
e3192690a   Joe Perches   net: Remove casts...
707
  			*p += CMSG_ALIGN(ipv6_optlen(*hdr));
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
708
709
710
711
712
713
  		}
  	} else {
  		if (newopt) {
  			if (copy_from_user(*p, newopt, newoptlen))
  				return -EFAULT;
  			*hdr = (struct ipv6_opt_hdr *)*p;
e3192690a   Joe Perches   net: Remove casts...
714
  			if (ipv6_optlen(*hdr) > newoptlen)
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
  				return -EINVAL;
  			*p += CMSG_ALIGN(newoptlen);
  		}
  	}
  	return 0;
  }
  
  struct ipv6_txoptions *
  ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt,
  		   int newtype,
  		   struct ipv6_opt_hdr __user *newopt, int newoptlen)
  {
  	int tot_len = 0;
  	char *p;
  	struct ipv6_txoptions *opt2;
  	int err;
99c7bc013   YOSHIFUJI Hideaki   [IPV6]: Fix kerne...
731
732
733
734
735
736
737
738
739
740
  	if (opt) {
  		if (newtype != IPV6_HOPOPTS && opt->hopopt)
  			tot_len += CMSG_ALIGN(ipv6_optlen(opt->hopopt));
  		if (newtype != IPV6_RTHDRDSTOPTS && opt->dst0opt)
  			tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst0opt));
  		if (newtype != IPV6_RTHDR && opt->srcrt)
  			tot_len += CMSG_ALIGN(ipv6_optlen(opt->srcrt));
  		if (newtype != IPV6_DSTOPTS && opt->dst1opt)
  			tot_len += CMSG_ALIGN(ipv6_optlen(opt->dst1opt));
  	}
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
741
742
743
744
745
  	if (newopt && newoptlen)
  		tot_len += CMSG_ALIGN(newoptlen);
  
  	if (!tot_len)
  		return NULL;
8b8aa4b5a   YOSHIFUJI Hideaki   [IPV6]: Fix memor...
746
  	tot_len += sizeof(*opt2);
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
747
748
749
750
751
752
753
754
  	opt2 = sock_kmalloc(sk, tot_len, GFP_ATOMIC);
  	if (!opt2)
  		return ERR_PTR(-ENOBUFS);
  
  	memset(opt2, 0, tot_len);
  
  	opt2->tot_len = tot_len;
  	p = (char *)(opt2 + 1);
99c7bc013   YOSHIFUJI Hideaki   [IPV6]: Fix kerne...
755
  	err = ipv6_renew_option(opt ? opt->hopopt : NULL, newopt, newoptlen,
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
756
757
758
759
  				newtype != IPV6_HOPOPTS,
  				&opt2->hopopt, &p);
  	if (err)
  		goto out;
99c7bc013   YOSHIFUJI Hideaki   [IPV6]: Fix kerne...
760
  	err = ipv6_renew_option(opt ? opt->dst0opt : NULL, newopt, newoptlen,
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
761
762
763
764
  				newtype != IPV6_RTHDRDSTOPTS,
  				&opt2->dst0opt, &p);
  	if (err)
  		goto out;
99c7bc013   YOSHIFUJI Hideaki   [IPV6]: Fix kerne...
765
  	err = ipv6_renew_option(opt ? opt->srcrt : NULL, newopt, newoptlen,
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
766
  				newtype != IPV6_RTHDR,
99c7bc013   YOSHIFUJI Hideaki   [IPV6]: Fix kerne...
767
  				(struct ipv6_opt_hdr **)&opt2->srcrt, &p);
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
768
769
  	if (err)
  		goto out;
99c7bc013   YOSHIFUJI Hideaki   [IPV6]: Fix kerne...
770
  	err = ipv6_renew_option(opt ? opt->dst1opt : NULL, newopt, newoptlen,
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
771
772
773
774
775
776
777
778
779
780
781
782
  				newtype != IPV6_DSTOPTS,
  				&opt2->dst1opt, &p);
  	if (err)
  		goto out;
  
  	opt2->opt_nflen = (opt2->hopopt ? ipv6_optlen(opt2->hopopt) : 0) +
  			  (opt2->dst0opt ? ipv6_optlen(opt2->dst0opt) : 0) +
  			  (opt2->srcrt ? ipv6_optlen(opt2->srcrt) : 0);
  	opt2->opt_flen = (opt2->dst1opt ? ipv6_optlen(opt2->dst1opt) : 0);
  
  	return opt2;
  out:
8b8aa4b5a   YOSHIFUJI Hideaki   [IPV6]: Fix memor...
783
  	sock_kfree_s(sk, opt2, opt2->tot_len);
333fad536   YOSHIFUJI Hideaki   [IPV6]: Support s...
784
785
  	return ERR_PTR(err);
  }
df9890c31   YOSHIFUJI Hideaki   [IPV6]: Fix sendi...
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
  struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space,
  					  struct ipv6_txoptions *opt)
  {
  	/*
  	 * ignore the dest before srcrt unless srcrt is being included.
  	 * --yoshfuji
  	 */
  	if (opt && opt->dst0opt && !opt->srcrt) {
  		if (opt_space != opt) {
  			memcpy(opt_space, opt, sizeof(*opt_space));
  			opt = opt_space;
  		}
  		opt->opt_nflen -= ipv6_optlen(opt->dst0opt);
  		opt->dst0opt = NULL;
  	}
  
  	return opt;
  }
a495f8364   Chris Elston   ipv6: Export ipv6...
804
  EXPORT_SYMBOL_GPL(ipv6_fixup_options);
df9890c31   YOSHIFUJI Hideaki   [IPV6]: Fix sendi...
805

20c59de2e   Arnaud Ebalard   ipv6: Refactor up...
806
807
808
809
  /**
   * fl6_update_dst - update flowi destination address with info given
   *                  by srcrt option, if any.
   *
4c9483b2f   David S. Miller   ipv6: Convert to ...
810
   * @fl6: flowi6 for which daddr is to be updated
20c59de2e   Arnaud Ebalard   ipv6: Refactor up...
811
   * @opt: struct ipv6_txoptions in which to look for srcrt opt
4c9483b2f   David S. Miller   ipv6: Convert to ...
812
   * @orig: copy of original daddr address if modified
20c59de2e   Arnaud Ebalard   ipv6: Refactor up...
813
814
   *
   * Returns NULL if no txoptions or no srcrt, otherwise returns orig
4c9483b2f   David S. Miller   ipv6: Convert to ...
815
   * and initial value of fl6->daddr set in orig
20c59de2e   Arnaud Ebalard   ipv6: Refactor up...
816
   */
4c9483b2f   David S. Miller   ipv6: Convert to ...
817
  struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
20c59de2e   Arnaud Ebalard   ipv6: Refactor up...
818
819
820
821
822
  				const struct ipv6_txoptions *opt,
  				struct in6_addr *orig)
  {
  	if (!opt || !opt->srcrt)
  		return NULL;
4e3fd7a06   Alexey Dobriyan   net: remove ipv6_...
823
824
  	*orig = fl6->daddr;
  	fl6->daddr = *((struct rt0_hdr *)opt->srcrt)->addr;
20c59de2e   Arnaud Ebalard   ipv6: Refactor up...
825
826
  	return orig;
  }
20c59de2e   Arnaud Ebalard   ipv6: Refactor up...
827
  EXPORT_SYMBOL_GPL(fl6_update_dst);