xref: /openbmc/linux/net/ipv6/mip6.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
22c8d7ca0SNoriaki TAKAMIYA /*
32c8d7ca0SNoriaki TAKAMIYA  * Copyright (C)2003-2006 Helsinki University of Technology
42c8d7ca0SNoriaki TAKAMIYA  * Copyright (C)2003-2006 USAGI/WIDE Project
52c8d7ca0SNoriaki TAKAMIYA  */
62c8d7ca0SNoriaki TAKAMIYA /*
72c8d7ca0SNoriaki TAKAMIYA  * Authors:
82c8d7ca0SNoriaki TAKAMIYA  *	Noriaki TAKAMIYA @USAGI
92c8d7ca0SNoriaki TAKAMIYA  *	Masahide NAKAMURA @USAGI
102c8d7ca0SNoriaki TAKAMIYA  */
112c8d7ca0SNoriaki TAKAMIYA 
12f3213831SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13f3213831SJoe Perches 
142c8d7ca0SNoriaki TAKAMIYA #include <linux/module.h>
152c8d7ca0SNoriaki TAKAMIYA #include <linux/skbuff.h>
1670182ed2SMasahide NAKAMURA #include <linux/time.h>
172c8d7ca0SNoriaki TAKAMIYA #include <linux/ipv6.h>
187be96f76SMasahide NAKAMURA #include <linux/icmpv6.h>
197be96f76SMasahide NAKAMURA #include <net/sock.h>
202c8d7ca0SNoriaki TAKAMIYA #include <net/ipv6.h>
217be96f76SMasahide NAKAMURA #include <net/ip6_checksum.h>
2259fbb3a6SMasahide NAKAMURA #include <net/rawv6.h>
232c8d7ca0SNoriaki TAKAMIYA #include <net/xfrm.h>
242c8d7ca0SNoriaki TAKAMIYA #include <net/mip6.h>
252c8d7ca0SNoriaki TAKAMIYA 
calc_padlen(unsigned int len,unsigned int n)263d126890SNoriaki TAKAMIYA static inline unsigned int calc_padlen(unsigned int len, unsigned int n)
273d126890SNoriaki TAKAMIYA {
283d126890SNoriaki TAKAMIYA 	return (n - len + 16) & 0x7;
293d126890SNoriaki TAKAMIYA }
303d126890SNoriaki TAKAMIYA 
mip6_padn(__u8 * data,__u8 padlen)313d126890SNoriaki TAKAMIYA static inline void *mip6_padn(__u8 *data, __u8 padlen)
323d126890SNoriaki TAKAMIYA {
333d126890SNoriaki TAKAMIYA 	if (!data)
343d126890SNoriaki TAKAMIYA 		return NULL;
353d126890SNoriaki TAKAMIYA 	if (padlen == 1) {
361de5a71cSEldad Zack 		data[0] = IPV6_TLV_PAD1;
373d126890SNoriaki TAKAMIYA 	} else if (padlen > 1) {
387f1eced8SYOSHIFUJI Hideaki 		data[0] = IPV6_TLV_PADN;
393d126890SNoriaki TAKAMIYA 		data[1] = padlen - 2;
403d126890SNoriaki TAKAMIYA 		if (padlen > 2)
413d126890SNoriaki TAKAMIYA 			memset(data+2, 0, data[1]);
423d126890SNoriaki TAKAMIYA 	}
433d126890SNoriaki TAKAMIYA 	return data + padlen;
443d126890SNoriaki TAKAMIYA }
453d126890SNoriaki TAKAMIYA 
mip6_param_prob(struct sk_buff * skb,u8 code,int pos)46d5fdd6baSBrian Haley static inline void mip6_param_prob(struct sk_buff *skb, u8 code, int pos)
477be96f76SMasahide NAKAMURA {
483ffe533cSAlexey Dobriyan 	icmpv6_send(skb, ICMPV6_PARAMPROB, code, pos);
497be96f76SMasahide NAKAMURA }
507be96f76SMasahide NAKAMURA 
mip6_mh_len(int type)517be96f76SMasahide NAKAMURA static int mip6_mh_len(int type)
527be96f76SMasahide NAKAMURA {
537be96f76SMasahide NAKAMURA 	int len = 0;
547be96f76SMasahide NAKAMURA 
557be96f76SMasahide NAKAMURA 	switch (type) {
567be96f76SMasahide NAKAMURA 	case IP6_MH_TYPE_BRR:
577be96f76SMasahide NAKAMURA 		len = 0;
587be96f76SMasahide NAKAMURA 		break;
597be96f76SMasahide NAKAMURA 	case IP6_MH_TYPE_HOTI:
607be96f76SMasahide NAKAMURA 	case IP6_MH_TYPE_COTI:
617be96f76SMasahide NAKAMURA 	case IP6_MH_TYPE_BU:
627be96f76SMasahide NAKAMURA 	case IP6_MH_TYPE_BACK:
637be96f76SMasahide NAKAMURA 		len = 1;
647be96f76SMasahide NAKAMURA 		break;
657be96f76SMasahide NAKAMURA 	case IP6_MH_TYPE_HOT:
667be96f76SMasahide NAKAMURA 	case IP6_MH_TYPE_COT:
677be96f76SMasahide NAKAMURA 	case IP6_MH_TYPE_BERROR:
687be96f76SMasahide NAKAMURA 		len = 2;
697be96f76SMasahide NAKAMURA 		break;
707be96f76SMasahide NAKAMURA 	}
717be96f76SMasahide NAKAMURA 	return len;
727be96f76SMasahide NAKAMURA }
737be96f76SMasahide NAKAMURA 
mip6_mh_filter(struct sock * sk,struct sk_buff * skb)7459fbb3a6SMasahide NAKAMURA static int mip6_mh_filter(struct sock *sk, struct sk_buff *skb)
757be96f76SMasahide NAKAMURA {
7696af69eaSEric Dumazet 	struct ip6_mh _hdr;
7796af69eaSEric Dumazet 	const struct ip6_mh *mh;
787be96f76SMasahide NAKAMURA 
7996af69eaSEric Dumazet 	mh = skb_header_pointer(skb, skb_transport_offset(skb),
8096af69eaSEric Dumazet 				sizeof(_hdr), &_hdr);
8196af69eaSEric Dumazet 	if (!mh)
827be96f76SMasahide NAKAMURA 		return -1;
837be96f76SMasahide NAKAMURA 
8496af69eaSEric Dumazet 	if (((mh->ip6mh_hdrlen + 1) << 3) > skb->len)
8596af69eaSEric Dumazet 		return -1;
867be96f76SMasahide NAKAMURA 
877be96f76SMasahide NAKAMURA 	if (mh->ip6mh_hdrlen < mip6_mh_len(mh->ip6mh_type)) {
88ba7a46f1SJoe Perches 		net_dbg_ratelimited("mip6: MH message too short: %d vs >=%d\n",
89ba7a46f1SJoe Perches 				    mh->ip6mh_hdrlen,
90ba7a46f1SJoe Perches 				    mip6_mh_len(mh->ip6mh_type));
9196af69eaSEric Dumazet 		mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_hdrlen) +
9296af69eaSEric Dumazet 				skb_network_header_len(skb));
937be96f76SMasahide NAKAMURA 		return -1;
947be96f76SMasahide NAKAMURA 	}
957be96f76SMasahide NAKAMURA 
967be96f76SMasahide NAKAMURA 	if (mh->ip6mh_proto != IPPROTO_NONE) {
97ba7a46f1SJoe Perches 		net_dbg_ratelimited("mip6: MH invalid payload proto = %d\n",
987be96f76SMasahide NAKAMURA 				    mh->ip6mh_proto);
9996af69eaSEric Dumazet 		mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_proto) +
10096af69eaSEric Dumazet 				skb_network_header_len(skb));
1017be96f76SMasahide NAKAMURA 		return -1;
1027be96f76SMasahide NAKAMURA 	}
1037be96f76SMasahide NAKAMURA 
1047be96f76SMasahide NAKAMURA 	return 0;
1057be96f76SMasahide NAKAMURA }
1067be96f76SMasahide NAKAMURA 
10770182ed2SMasahide NAKAMURA struct mip6_report_rate_limiter {
10870182ed2SMasahide NAKAMURA 	spinlock_t lock;
1093dd7669fSArnd Bergmann 	ktime_t stamp;
11070182ed2SMasahide NAKAMURA 	int iif;
11170182ed2SMasahide NAKAMURA 	struct in6_addr src;
11270182ed2SMasahide NAKAMURA 	struct in6_addr dst;
11370182ed2SMasahide NAKAMURA };
11470182ed2SMasahide NAKAMURA 
11570182ed2SMasahide NAKAMURA static struct mip6_report_rate_limiter mip6_report_rl = {
1164ef8d0aeSMilind Arun Choudhary 	.lock = __SPIN_LOCK_UNLOCKED(mip6_report_rl.lock)
11770182ed2SMasahide NAKAMURA };
11870182ed2SMasahide NAKAMURA 
mip6_destopt_input(struct xfrm_state * x,struct sk_buff * skb)1193d126890SNoriaki TAKAMIYA static int mip6_destopt_input(struct xfrm_state *x, struct sk_buff *skb)
1203d126890SNoriaki TAKAMIYA {
121b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
1223d126890SNoriaki TAKAMIYA 	struct ipv6_destopt_hdr *destopt = (struct ipv6_destopt_hdr *)skb->data;
1230ebea8efSHerbert Xu 	int err = destopt->nexthdr;
1243d126890SNoriaki TAKAMIYA 
1250ebea8efSHerbert Xu 	spin_lock(&x->lock);
1263d126890SNoriaki TAKAMIYA 	if (!ipv6_addr_equal(&iph->saddr, (struct in6_addr *)x->coaddr) &&
1273d126890SNoriaki TAKAMIYA 	    !ipv6_addr_any((struct in6_addr *)x->coaddr))
1280ebea8efSHerbert Xu 		err = -ENOENT;
1290ebea8efSHerbert Xu 	spin_unlock(&x->lock);
1303d126890SNoriaki TAKAMIYA 
1310ebea8efSHerbert Xu 	return err;
1323d126890SNoriaki TAKAMIYA }
1333d126890SNoriaki TAKAMIYA 
1343d126890SNoriaki TAKAMIYA /* Destination Option Header is inserted.
1353d126890SNoriaki TAKAMIYA  * IP Header's src address is replaced with Home Address Option in
1363d126890SNoriaki TAKAMIYA  * Destination Option Header.
1373d126890SNoriaki TAKAMIYA  */
mip6_destopt_output(struct xfrm_state * x,struct sk_buff * skb)1383d126890SNoriaki TAKAMIYA static int mip6_destopt_output(struct xfrm_state *x, struct sk_buff *skb)
1393d126890SNoriaki TAKAMIYA {
1403d126890SNoriaki TAKAMIYA 	struct ipv6hdr *iph;
1413d126890SNoriaki TAKAMIYA 	struct ipv6_destopt_hdr *dstopt;
1423d126890SNoriaki TAKAMIYA 	struct ipv6_destopt_hao *hao;
1433d126890SNoriaki TAKAMIYA 	u8 nexthdr;
1443d126890SNoriaki TAKAMIYA 	int len;
1453d126890SNoriaki TAKAMIYA 
1467b277b1aSHerbert Xu 	skb_push(skb, -skb_network_offset(skb));
147007f0211SHerbert Xu 	iph = ipv6_hdr(skb);
1483d126890SNoriaki TAKAMIYA 
149007f0211SHerbert Xu 	nexthdr = *skb_mac_header(skb);
150007f0211SHerbert Xu 	*skb_mac_header(skb) = IPPROTO_DSTOPTS;
1513d126890SNoriaki TAKAMIYA 
1529c70220bSArnaldo Carvalho de Melo 	dstopt = (struct ipv6_destopt_hdr *)skb_transport_header(skb);
1533d126890SNoriaki TAKAMIYA 	dstopt->nexthdr = nexthdr;
1543d126890SNoriaki TAKAMIYA 
1553d126890SNoriaki TAKAMIYA 	hao = mip6_padn((char *)(dstopt + 1),
1563d126890SNoriaki TAKAMIYA 			calc_padlen(sizeof(*dstopt), 6));
1573d126890SNoriaki TAKAMIYA 
1583d126890SNoriaki TAKAMIYA 	hao->type = IPV6_TLV_HAO;
159547b792cSIlpo Järvinen 	BUILD_BUG_ON(sizeof(*hao) != 18);
1603d126890SNoriaki TAKAMIYA 	hao->length = sizeof(*hao) - 2;
1613d126890SNoriaki TAKAMIYA 
1623d126890SNoriaki TAKAMIYA 	len = ((char *)hao - (char *)dstopt) + sizeof(*hao);
1633d126890SNoriaki TAKAMIYA 
1643d126890SNoriaki TAKAMIYA 	memcpy(&hao->addr, &iph->saddr, sizeof(hao->addr));
165b7c6538cSHerbert Xu 	spin_lock_bh(&x->lock);
1663d126890SNoriaki TAKAMIYA 	memcpy(&iph->saddr, x->coaddr, sizeof(iph->saddr));
167b7c6538cSHerbert Xu 	spin_unlock_bh(&x->lock);
1683d126890SNoriaki TAKAMIYA 
169547b792cSIlpo Järvinen 	WARN_ON(len != x->props.header_len);
1703d126890SNoriaki TAKAMIYA 	dstopt->hdrlen = (x->props.header_len >> 3) - 1;
1713d126890SNoriaki TAKAMIYA 
1723d126890SNoriaki TAKAMIYA 	return 0;
1733d126890SNoriaki TAKAMIYA }
1743d126890SNoriaki TAKAMIYA 
mip6_report_rl_allow(ktime_t stamp,const struct in6_addr * dst,const struct in6_addr * src,int iif)1753dd7669fSArnd Bergmann static inline int mip6_report_rl_allow(ktime_t stamp,
176b71d1d42SEric Dumazet 				       const struct in6_addr *dst,
177b71d1d42SEric Dumazet 				       const struct in6_addr *src, int iif)
17870182ed2SMasahide NAKAMURA {
17970182ed2SMasahide NAKAMURA 	int allow = 0;
18070182ed2SMasahide NAKAMURA 
18170182ed2SMasahide NAKAMURA 	spin_lock_bh(&mip6_report_rl.lock);
1821f3a8e49SThomas Gleixner 	if (mip6_report_rl.stamp != stamp ||
18370182ed2SMasahide NAKAMURA 	    mip6_report_rl.iif != iif ||
18470182ed2SMasahide NAKAMURA 	    !ipv6_addr_equal(&mip6_report_rl.src, src) ||
18570182ed2SMasahide NAKAMURA 	    !ipv6_addr_equal(&mip6_report_rl.dst, dst)) {
1863dd7669fSArnd Bergmann 		mip6_report_rl.stamp = stamp;
18770182ed2SMasahide NAKAMURA 		mip6_report_rl.iif = iif;
1884e3fd7a0SAlexey Dobriyan 		mip6_report_rl.src = *src;
1894e3fd7a0SAlexey Dobriyan 		mip6_report_rl.dst = *dst;
19070182ed2SMasahide NAKAMURA 		allow = 1;
19170182ed2SMasahide NAKAMURA 	}
19270182ed2SMasahide NAKAMURA 	spin_unlock_bh(&mip6_report_rl.lock);
19370182ed2SMasahide NAKAMURA 	return allow;
19470182ed2SMasahide NAKAMURA }
19570182ed2SMasahide NAKAMURA 
mip6_destopt_reject(struct xfrm_state * x,struct sk_buff * skb,const struct flowi * fl)1968f029de2SDavid S. Miller static int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb,
1978f029de2SDavid S. Miller 			       const struct flowi *fl)
19870182ed2SMasahide NAKAMURA {
199db983c11SAlexey Dobriyan 	struct net *net = xs_net(x);
20070182ed2SMasahide NAKAMURA 	struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
2014c9483b2SDavid S. Miller 	const struct flowi6 *fl6 = &fl->u.ip6;
20270182ed2SMasahide NAKAMURA 	struct ipv6_destopt_hao *hao = NULL;
20370182ed2SMasahide NAKAMURA 	struct xfrm_selector sel;
20470182ed2SMasahide NAKAMURA 	int offset;
2053dd7669fSArnd Bergmann 	ktime_t stamp;
20670182ed2SMasahide NAKAMURA 	int err = 0;
20770182ed2SMasahide NAKAMURA 
2084c9483b2SDavid S. Miller 	if (unlikely(fl6->flowi6_proto == IPPROTO_MH &&
2091958b856SDavid S. Miller 		     fl6->fl6_mh_type <= IP6_MH_TYPE_MAX))
21001be8e5dSMasahide NAKAMURA 		goto out;
21101be8e5dSMasahide NAKAMURA 
21270182ed2SMasahide NAKAMURA 	if (likely(opt->dsthao)) {
21370182ed2SMasahide NAKAMURA 		offset = ipv6_find_tlv(skb, opt->dsthao, IPV6_TLV_HAO);
21470182ed2SMasahide NAKAMURA 		if (likely(offset >= 0))
215d56f90a7SArnaldo Carvalho de Melo 			hao = (struct ipv6_destopt_hao *)
216d56f90a7SArnaldo Carvalho de Melo 					(skb_network_header(skb) + offset);
21770182ed2SMasahide NAKAMURA 	}
21870182ed2SMasahide NAKAMURA 
2193dd7669fSArnd Bergmann 	stamp = skb_get_ktime(skb);
22070182ed2SMasahide NAKAMURA 
2213dd7669fSArnd Bergmann 	if (!mip6_report_rl_allow(stamp, &ipv6_hdr(skb)->daddr,
2220660e03fSArnaldo Carvalho de Melo 				  hao ? &hao->addr : &ipv6_hdr(skb)->saddr,
22370182ed2SMasahide NAKAMURA 				  opt->iif))
22470182ed2SMasahide NAKAMURA 		goto out;
22570182ed2SMasahide NAKAMURA 
22670182ed2SMasahide NAKAMURA 	memset(&sel, 0, sizeof(sel));
2270660e03fSArnaldo Carvalho de Melo 	memcpy(&sel.daddr, (xfrm_address_t *)&ipv6_hdr(skb)->daddr,
22870182ed2SMasahide NAKAMURA 	       sizeof(sel.daddr));
22970182ed2SMasahide NAKAMURA 	sel.prefixlen_d = 128;
2300660e03fSArnaldo Carvalho de Melo 	memcpy(&sel.saddr, (xfrm_address_t *)&ipv6_hdr(skb)->saddr,
23170182ed2SMasahide NAKAMURA 	       sizeof(sel.saddr));
23270182ed2SMasahide NAKAMURA 	sel.prefixlen_s = 128;
23370182ed2SMasahide NAKAMURA 	sel.family = AF_INET6;
2344c9483b2SDavid S. Miller 	sel.proto = fl6->flowi6_proto;
2354c9483b2SDavid S. Miller 	sel.dport = xfrm_flowi_dport(fl, &fl6->uli);
23670182ed2SMasahide NAKAMURA 	if (sel.dport)
237e69a4adcSAl Viro 		sel.dport_mask = htons(~0);
2384c9483b2SDavid S. Miller 	sel.sport = xfrm_flowi_sport(fl, &fl6->uli);
23970182ed2SMasahide NAKAMURA 	if (sel.sport)
240e69a4adcSAl Viro 		sel.sport_mask = htons(~0);
2414c9483b2SDavid S. Miller 	sel.ifindex = fl6->flowi6_oif;
24270182ed2SMasahide NAKAMURA 
243db983c11SAlexey Dobriyan 	err = km_report(net, IPPROTO_DSTOPTS, &sel,
24470182ed2SMasahide NAKAMURA 			(hao ? (xfrm_address_t *)&hao->addr : NULL));
24570182ed2SMasahide NAKAMURA 
24670182ed2SMasahide NAKAMURA  out:
24770182ed2SMasahide NAKAMURA 	return err;
24870182ed2SMasahide NAKAMURA }
24970182ed2SMasahide NAKAMURA 
mip6_destopt_init_state(struct xfrm_state * x,struct netlink_ext_ack * extack)250e1e10b44SSabrina Dubroca static int mip6_destopt_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
2513d126890SNoriaki TAKAMIYA {
2523d126890SNoriaki TAKAMIYA 	if (x->id.spi) {
253*28b5dbd5SSabrina Dubroca 		NL_SET_ERR_MSG(extack, "SPI must be 0");
2543d126890SNoriaki TAKAMIYA 		return -EINVAL;
2553d126890SNoriaki TAKAMIYA 	}
2563d126890SNoriaki TAKAMIYA 	if (x->props.mode != XFRM_MODE_ROUTEOPTIMIZATION) {
257*28b5dbd5SSabrina Dubroca 		NL_SET_ERR_MSG(extack, "XFRM mode must be XFRM_MODE_ROUTEOPTIMIZATION");
2583d126890SNoriaki TAKAMIYA 		return -EINVAL;
2593d126890SNoriaki TAKAMIYA 	}
2603d126890SNoriaki TAKAMIYA 
2613d126890SNoriaki TAKAMIYA 	x->props.header_len = sizeof(struct ipv6_destopt_hdr) +
2623d126890SNoriaki TAKAMIYA 		calc_padlen(sizeof(struct ipv6_destopt_hdr), 6) +
2633d126890SNoriaki TAKAMIYA 		sizeof(struct ipv6_destopt_hao);
264547b792cSIlpo Järvinen 	WARN_ON(x->props.header_len != 24);
2653d126890SNoriaki TAKAMIYA 
2663d126890SNoriaki TAKAMIYA 	return 0;
2673d126890SNoriaki TAKAMIYA }
2683d126890SNoriaki TAKAMIYA 
2693d126890SNoriaki TAKAMIYA /*
2703d126890SNoriaki TAKAMIYA  * Do nothing about destroying since it has no specific operation for
2713d126890SNoriaki TAKAMIYA  * destination options header unlike IPsec protocols.
2723d126890SNoriaki TAKAMIYA  */
mip6_destopt_destroy(struct xfrm_state * x)2733d126890SNoriaki TAKAMIYA static void mip6_destopt_destroy(struct xfrm_state *x)
2743d126890SNoriaki TAKAMIYA {
2753d126890SNoriaki TAKAMIYA }
2763d126890SNoriaki TAKAMIYA 
277cc24becaSIan Morris static const struct xfrm_type mip6_destopt_type = {
2783d126890SNoriaki TAKAMIYA 	.owner		= THIS_MODULE,
2793d126890SNoriaki TAKAMIYA 	.proto		= IPPROTO_DSTOPTS,
280f04e7e8dSHerbert Xu 	.flags		= XFRM_TYPE_NON_FRAGMENT | XFRM_TYPE_LOCAL_COADDR,
2813d126890SNoriaki TAKAMIYA 	.init_state	= mip6_destopt_init_state,
2823d126890SNoriaki TAKAMIYA 	.destructor	= mip6_destopt_destroy,
2833d126890SNoriaki TAKAMIYA 	.input		= mip6_destopt_input,
2843d126890SNoriaki TAKAMIYA 	.output		= mip6_destopt_output,
28570182ed2SMasahide NAKAMURA 	.reject		= mip6_destopt_reject,
2863d126890SNoriaki TAKAMIYA };
2873d126890SNoriaki TAKAMIYA 
mip6_rthdr_input(struct xfrm_state * x,struct sk_buff * skb)2882c8d7ca0SNoriaki TAKAMIYA static int mip6_rthdr_input(struct xfrm_state *x, struct sk_buff *skb)
2892c8d7ca0SNoriaki TAKAMIYA {
290b71d1d42SEric Dumazet 	const struct ipv6hdr *iph = ipv6_hdr(skb);
2912c8d7ca0SNoriaki TAKAMIYA 	struct rt2_hdr *rt2 = (struct rt2_hdr *)skb->data;
2920ebea8efSHerbert Xu 	int err = rt2->rt_hdr.nexthdr;
2932c8d7ca0SNoriaki TAKAMIYA 
2940ebea8efSHerbert Xu 	spin_lock(&x->lock);
295d9a9dc66SArnaud Ebalard 	if (!ipv6_addr_equal(&iph->daddr, (struct in6_addr *)x->coaddr) &&
2962c8d7ca0SNoriaki TAKAMIYA 	    !ipv6_addr_any((struct in6_addr *)x->coaddr))
2970ebea8efSHerbert Xu 		err = -ENOENT;
2980ebea8efSHerbert Xu 	spin_unlock(&x->lock);
2992c8d7ca0SNoriaki TAKAMIYA 
3000ebea8efSHerbert Xu 	return err;
3012c8d7ca0SNoriaki TAKAMIYA }
3022c8d7ca0SNoriaki TAKAMIYA 
3032c8d7ca0SNoriaki TAKAMIYA /* Routing Header type 2 is inserted.
3042c8d7ca0SNoriaki TAKAMIYA  * IP Header's dst address is replaced with Routing Header's Home Address.
3052c8d7ca0SNoriaki TAKAMIYA  */
mip6_rthdr_output(struct xfrm_state * x,struct sk_buff * skb)3062c8d7ca0SNoriaki TAKAMIYA static int mip6_rthdr_output(struct xfrm_state *x, struct sk_buff *skb)
3072c8d7ca0SNoriaki TAKAMIYA {
3082c8d7ca0SNoriaki TAKAMIYA 	struct ipv6hdr *iph;
3092c8d7ca0SNoriaki TAKAMIYA 	struct rt2_hdr *rt2;
3102c8d7ca0SNoriaki TAKAMIYA 	u8 nexthdr;
3112c8d7ca0SNoriaki TAKAMIYA 
3127b277b1aSHerbert Xu 	skb_push(skb, -skb_network_offset(skb));
313007f0211SHerbert Xu 	iph = ipv6_hdr(skb);
3142c8d7ca0SNoriaki TAKAMIYA 
315007f0211SHerbert Xu 	nexthdr = *skb_mac_header(skb);
316007f0211SHerbert Xu 	*skb_mac_header(skb) = IPPROTO_ROUTING;
3172c8d7ca0SNoriaki TAKAMIYA 
3189c70220bSArnaldo Carvalho de Melo 	rt2 = (struct rt2_hdr *)skb_transport_header(skb);
3192c8d7ca0SNoriaki TAKAMIYA 	rt2->rt_hdr.nexthdr = nexthdr;
3202c8d7ca0SNoriaki TAKAMIYA 	rt2->rt_hdr.hdrlen = (x->props.header_len >> 3) - 1;
3212c8d7ca0SNoriaki TAKAMIYA 	rt2->rt_hdr.type = IPV6_SRCRT_TYPE_2;
3222c8d7ca0SNoriaki TAKAMIYA 	rt2->rt_hdr.segments_left = 1;
3232c8d7ca0SNoriaki TAKAMIYA 	memset(&rt2->reserved, 0, sizeof(rt2->reserved));
3242c8d7ca0SNoriaki TAKAMIYA 
325547b792cSIlpo Järvinen 	WARN_ON(rt2->rt_hdr.hdrlen != 2);
3262c8d7ca0SNoriaki TAKAMIYA 
3272c8d7ca0SNoriaki TAKAMIYA 	memcpy(&rt2->addr, &iph->daddr, sizeof(rt2->addr));
328b7c6538cSHerbert Xu 	spin_lock_bh(&x->lock);
3292c8d7ca0SNoriaki TAKAMIYA 	memcpy(&iph->daddr, x->coaddr, sizeof(iph->daddr));
330b7c6538cSHerbert Xu 	spin_unlock_bh(&x->lock);
3312c8d7ca0SNoriaki TAKAMIYA 
3322c8d7ca0SNoriaki TAKAMIYA 	return 0;
3332c8d7ca0SNoriaki TAKAMIYA }
3342c8d7ca0SNoriaki TAKAMIYA 
mip6_rthdr_init_state(struct xfrm_state * x,struct netlink_ext_ack * extack)335e1e10b44SSabrina Dubroca static int mip6_rthdr_init_state(struct xfrm_state *x, struct netlink_ext_ack *extack)
3362c8d7ca0SNoriaki TAKAMIYA {
3372c8d7ca0SNoriaki TAKAMIYA 	if (x->id.spi) {
338*28b5dbd5SSabrina Dubroca 		NL_SET_ERR_MSG(extack, "SPI must be 0");
3392c8d7ca0SNoriaki TAKAMIYA 		return -EINVAL;
3402c8d7ca0SNoriaki TAKAMIYA 	}
3412c8d7ca0SNoriaki TAKAMIYA 	if (x->props.mode != XFRM_MODE_ROUTEOPTIMIZATION) {
342*28b5dbd5SSabrina Dubroca 		NL_SET_ERR_MSG(extack, "XFRM mode must be XFRM_MODE_ROUTEOPTIMIZATION");
3432c8d7ca0SNoriaki TAKAMIYA 		return -EINVAL;
3442c8d7ca0SNoriaki TAKAMIYA 	}
3452c8d7ca0SNoriaki TAKAMIYA 
3462c8d7ca0SNoriaki TAKAMIYA 	x->props.header_len = sizeof(struct rt2_hdr);
3472c8d7ca0SNoriaki TAKAMIYA 
3482c8d7ca0SNoriaki TAKAMIYA 	return 0;
3492c8d7ca0SNoriaki TAKAMIYA }
3502c8d7ca0SNoriaki TAKAMIYA 
3512c8d7ca0SNoriaki TAKAMIYA /*
3522c8d7ca0SNoriaki TAKAMIYA  * Do nothing about destroying since it has no specific operation for routing
3532c8d7ca0SNoriaki TAKAMIYA  * header type 2 unlike IPsec protocols.
3542c8d7ca0SNoriaki TAKAMIYA  */
mip6_rthdr_destroy(struct xfrm_state * x)3552c8d7ca0SNoriaki TAKAMIYA static void mip6_rthdr_destroy(struct xfrm_state *x)
3562c8d7ca0SNoriaki TAKAMIYA {
3572c8d7ca0SNoriaki TAKAMIYA }
3582c8d7ca0SNoriaki TAKAMIYA 
359cc24becaSIan Morris static const struct xfrm_type mip6_rthdr_type = {
3602c8d7ca0SNoriaki TAKAMIYA 	.owner		= THIS_MODULE,
3612c8d7ca0SNoriaki TAKAMIYA 	.proto		= IPPROTO_ROUTING,
362f04e7e8dSHerbert Xu 	.flags		= XFRM_TYPE_NON_FRAGMENT | XFRM_TYPE_REMOTE_COADDR,
3632c8d7ca0SNoriaki TAKAMIYA 	.init_state	= mip6_rthdr_init_state,
3642c8d7ca0SNoriaki TAKAMIYA 	.destructor	= mip6_rthdr_destroy,
3652c8d7ca0SNoriaki TAKAMIYA 	.input		= mip6_rthdr_input,
3662c8d7ca0SNoriaki TAKAMIYA 	.output		= mip6_rthdr_output,
3672c8d7ca0SNoriaki TAKAMIYA };
3682c8d7ca0SNoriaki TAKAMIYA 
mip6_init(void)36959fbb3a6SMasahide NAKAMURA static int __init mip6_init(void)
3702c8d7ca0SNoriaki TAKAMIYA {
371f3213831SJoe Perches 	pr_info("Mobile IPv6\n");
3722c8d7ca0SNoriaki TAKAMIYA 
3733d126890SNoriaki TAKAMIYA 	if (xfrm_register_type(&mip6_destopt_type, AF_INET6) < 0) {
374f3213831SJoe Perches 		pr_info("%s: can't add xfrm type(destopt)\n", __func__);
3753d126890SNoriaki TAKAMIYA 		goto mip6_destopt_xfrm_fail;
3763d126890SNoriaki TAKAMIYA 	}
3772c8d7ca0SNoriaki TAKAMIYA 	if (xfrm_register_type(&mip6_rthdr_type, AF_INET6) < 0) {
378f3213831SJoe Perches 		pr_info("%s: can't add xfrm type(rthdr)\n", __func__);
3792c8d7ca0SNoriaki TAKAMIYA 		goto mip6_rthdr_xfrm_fail;
3802c8d7ca0SNoriaki TAKAMIYA 	}
38159fbb3a6SMasahide NAKAMURA 	if (rawv6_mh_filter_register(mip6_mh_filter) < 0) {
382f3213831SJoe Perches 		pr_info("%s: can't add rawv6 mh filter\n", __func__);
38359fbb3a6SMasahide NAKAMURA 		goto mip6_rawv6_mh_fail;
38459fbb3a6SMasahide NAKAMURA 	}
38559fbb3a6SMasahide NAKAMURA 
38659fbb3a6SMasahide NAKAMURA 
3872c8d7ca0SNoriaki TAKAMIYA 	return 0;
3882c8d7ca0SNoriaki TAKAMIYA 
38959fbb3a6SMasahide NAKAMURA  mip6_rawv6_mh_fail:
39059fbb3a6SMasahide NAKAMURA 	xfrm_unregister_type(&mip6_rthdr_type, AF_INET6);
3912c8d7ca0SNoriaki TAKAMIYA  mip6_rthdr_xfrm_fail:
3923d126890SNoriaki TAKAMIYA 	xfrm_unregister_type(&mip6_destopt_type, AF_INET6);
3933d126890SNoriaki TAKAMIYA  mip6_destopt_xfrm_fail:
3942c8d7ca0SNoriaki TAKAMIYA 	return -EAGAIN;
3952c8d7ca0SNoriaki TAKAMIYA }
3962c8d7ca0SNoriaki TAKAMIYA 
mip6_fini(void)39759fbb3a6SMasahide NAKAMURA static void __exit mip6_fini(void)
3982c8d7ca0SNoriaki TAKAMIYA {
39959fbb3a6SMasahide NAKAMURA 	if (rawv6_mh_filter_unregister(mip6_mh_filter) < 0)
400f3213831SJoe Perches 		pr_info("%s: can't remove rawv6 mh filter\n", __func__);
4014f518e80SFlorian Westphal 	xfrm_unregister_type(&mip6_rthdr_type, AF_INET6);
4024f518e80SFlorian Westphal 	xfrm_unregister_type(&mip6_destopt_type, AF_INET6);
4032c8d7ca0SNoriaki TAKAMIYA }
40459fbb3a6SMasahide NAKAMURA 
40559fbb3a6SMasahide NAKAMURA module_init(mip6_init);
40659fbb3a6SMasahide NAKAMURA module_exit(mip6_fini);
40759fbb3a6SMasahide NAKAMURA 
40859fbb3a6SMasahide NAKAMURA MODULE_LICENSE("GPL");
409d3d6dd3aSMasahide NAKAMURA MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_DSTOPTS);
410d3d6dd3aSMasahide NAKAMURA MODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_ROUTING);
411