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