18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C)2003-2006 Helsinki University of Technology 48c2ecf20Sopenharmony_ci * Copyright (C)2003-2006 USAGI/WIDE Project 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci/* 78c2ecf20Sopenharmony_ci * Authors: 88c2ecf20Sopenharmony_ci * Noriaki TAKAMIYA @USAGI 98c2ecf20Sopenharmony_ci * Masahide NAKAMURA @USAGI 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 168c2ecf20Sopenharmony_ci#include <linux/time.h> 178c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 188c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 198c2ecf20Sopenharmony_ci#include <net/sock.h> 208c2ecf20Sopenharmony_ci#include <net/ipv6.h> 218c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 228c2ecf20Sopenharmony_ci#include <net/rawv6.h> 238c2ecf20Sopenharmony_ci#include <net/xfrm.h> 248c2ecf20Sopenharmony_ci#include <net/mip6.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic inline unsigned int calc_padlen(unsigned int len, unsigned int n) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci return (n - len + 16) & 0x7; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic inline void *mip6_padn(__u8 *data, __u8 padlen) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci if (!data) 348c2ecf20Sopenharmony_ci return NULL; 358c2ecf20Sopenharmony_ci if (padlen == 1) { 368c2ecf20Sopenharmony_ci data[0] = IPV6_TLV_PAD1; 378c2ecf20Sopenharmony_ci } else if (padlen > 1) { 388c2ecf20Sopenharmony_ci data[0] = IPV6_TLV_PADN; 398c2ecf20Sopenharmony_ci data[1] = padlen - 2; 408c2ecf20Sopenharmony_ci if (padlen > 2) 418c2ecf20Sopenharmony_ci memset(data+2, 0, data[1]); 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci return data + padlen; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic inline void mip6_param_prob(struct sk_buff *skb, u8 code, int pos) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci icmpv6_send(skb, ICMPV6_PARAMPROB, code, pos); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int mip6_mh_len(int type) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci int len = 0; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci switch (type) { 568c2ecf20Sopenharmony_ci case IP6_MH_TYPE_BRR: 578c2ecf20Sopenharmony_ci len = 0; 588c2ecf20Sopenharmony_ci break; 598c2ecf20Sopenharmony_ci case IP6_MH_TYPE_HOTI: 608c2ecf20Sopenharmony_ci case IP6_MH_TYPE_COTI: 618c2ecf20Sopenharmony_ci case IP6_MH_TYPE_BU: 628c2ecf20Sopenharmony_ci case IP6_MH_TYPE_BACK: 638c2ecf20Sopenharmony_ci len = 1; 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci case IP6_MH_TYPE_HOT: 668c2ecf20Sopenharmony_ci case IP6_MH_TYPE_COT: 678c2ecf20Sopenharmony_ci case IP6_MH_TYPE_BERROR: 688c2ecf20Sopenharmony_ci len = 2; 698c2ecf20Sopenharmony_ci break; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci return len; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int mip6_mh_filter(struct sock *sk, struct sk_buff *skb) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct ip6_mh _hdr; 778c2ecf20Sopenharmony_ci const struct ip6_mh *mh; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci mh = skb_header_pointer(skb, skb_transport_offset(skb), 808c2ecf20Sopenharmony_ci sizeof(_hdr), &_hdr); 818c2ecf20Sopenharmony_ci if (!mh) 828c2ecf20Sopenharmony_ci return -1; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (((mh->ip6mh_hdrlen + 1) << 3) > skb->len) 858c2ecf20Sopenharmony_ci return -1; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (mh->ip6mh_hdrlen < mip6_mh_len(mh->ip6mh_type)) { 888c2ecf20Sopenharmony_ci net_dbg_ratelimited("mip6: MH message too short: %d vs >=%d\n", 898c2ecf20Sopenharmony_ci mh->ip6mh_hdrlen, 908c2ecf20Sopenharmony_ci mip6_mh_len(mh->ip6mh_type)); 918c2ecf20Sopenharmony_ci mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_hdrlen) + 928c2ecf20Sopenharmony_ci skb_network_header_len(skb)); 938c2ecf20Sopenharmony_ci return -1; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (mh->ip6mh_proto != IPPROTO_NONE) { 978c2ecf20Sopenharmony_ci net_dbg_ratelimited("mip6: MH invalid payload proto = %d\n", 988c2ecf20Sopenharmony_ci mh->ip6mh_proto); 998c2ecf20Sopenharmony_ci mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_proto) + 1008c2ecf20Sopenharmony_ci skb_network_header_len(skb)); 1018c2ecf20Sopenharmony_ci return -1; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistruct mip6_report_rate_limiter { 1088c2ecf20Sopenharmony_ci spinlock_t lock; 1098c2ecf20Sopenharmony_ci ktime_t stamp; 1108c2ecf20Sopenharmony_ci int iif; 1118c2ecf20Sopenharmony_ci struct in6_addr src; 1128c2ecf20Sopenharmony_ci struct in6_addr dst; 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic struct mip6_report_rate_limiter mip6_report_rl = { 1168c2ecf20Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(mip6_report_rl.lock) 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int mip6_destopt_input(struct xfrm_state *x, struct sk_buff *skb) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci const struct ipv6hdr *iph = ipv6_hdr(skb); 1228c2ecf20Sopenharmony_ci struct ipv6_destopt_hdr *destopt = (struct ipv6_destopt_hdr *)skb->data; 1238c2ecf20Sopenharmony_ci int err = destopt->nexthdr; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci spin_lock(&x->lock); 1268c2ecf20Sopenharmony_ci if (!ipv6_addr_equal(&iph->saddr, (struct in6_addr *)x->coaddr) && 1278c2ecf20Sopenharmony_ci !ipv6_addr_any((struct in6_addr *)x->coaddr)) 1288c2ecf20Sopenharmony_ci err = -ENOENT; 1298c2ecf20Sopenharmony_ci spin_unlock(&x->lock); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return err; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* Destination Option Header is inserted. 1358c2ecf20Sopenharmony_ci * IP Header's src address is replaced with Home Address Option in 1368c2ecf20Sopenharmony_ci * Destination Option Header. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_cistatic int mip6_destopt_output(struct xfrm_state *x, struct sk_buff *skb) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct ipv6hdr *iph; 1418c2ecf20Sopenharmony_ci struct ipv6_destopt_hdr *dstopt; 1428c2ecf20Sopenharmony_ci struct ipv6_destopt_hao *hao; 1438c2ecf20Sopenharmony_ci u8 nexthdr; 1448c2ecf20Sopenharmony_ci int len; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci skb_push(skb, -skb_network_offset(skb)); 1478c2ecf20Sopenharmony_ci iph = ipv6_hdr(skb); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci nexthdr = *skb_mac_header(skb); 1508c2ecf20Sopenharmony_ci *skb_mac_header(skb) = IPPROTO_DSTOPTS; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci dstopt = (struct ipv6_destopt_hdr *)skb_transport_header(skb); 1538c2ecf20Sopenharmony_ci dstopt->nexthdr = nexthdr; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci hao = mip6_padn((char *)(dstopt + 1), 1568c2ecf20Sopenharmony_ci calc_padlen(sizeof(*dstopt), 6)); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci hao->type = IPV6_TLV_HAO; 1598c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(*hao) != 18); 1608c2ecf20Sopenharmony_ci hao->length = sizeof(*hao) - 2; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci len = ((char *)hao - (char *)dstopt) + sizeof(*hao); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci memcpy(&hao->addr, &iph->saddr, sizeof(hao->addr)); 1658c2ecf20Sopenharmony_ci spin_lock_bh(&x->lock); 1668c2ecf20Sopenharmony_ci memcpy(&iph->saddr, x->coaddr, sizeof(iph->saddr)); 1678c2ecf20Sopenharmony_ci spin_unlock_bh(&x->lock); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci WARN_ON(len != x->props.header_len); 1708c2ecf20Sopenharmony_ci dstopt->hdrlen = (x->props.header_len >> 3) - 1; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic inline int mip6_report_rl_allow(ktime_t stamp, 1768c2ecf20Sopenharmony_ci const struct in6_addr *dst, 1778c2ecf20Sopenharmony_ci const struct in6_addr *src, int iif) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci int allow = 0; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci spin_lock_bh(&mip6_report_rl.lock); 1828c2ecf20Sopenharmony_ci if (mip6_report_rl.stamp != stamp || 1838c2ecf20Sopenharmony_ci mip6_report_rl.iif != iif || 1848c2ecf20Sopenharmony_ci !ipv6_addr_equal(&mip6_report_rl.src, src) || 1858c2ecf20Sopenharmony_ci !ipv6_addr_equal(&mip6_report_rl.dst, dst)) { 1868c2ecf20Sopenharmony_ci mip6_report_rl.stamp = stamp; 1878c2ecf20Sopenharmony_ci mip6_report_rl.iif = iif; 1888c2ecf20Sopenharmony_ci mip6_report_rl.src = *src; 1898c2ecf20Sopenharmony_ci mip6_report_rl.dst = *dst; 1908c2ecf20Sopenharmony_ci allow = 1; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci spin_unlock_bh(&mip6_report_rl.lock); 1938c2ecf20Sopenharmony_ci return allow; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int mip6_destopt_reject(struct xfrm_state *x, struct sk_buff *skb, 1978c2ecf20Sopenharmony_ci const struct flowi *fl) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct net *net = xs_net(x); 2008c2ecf20Sopenharmony_ci struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; 2018c2ecf20Sopenharmony_ci const struct flowi6 *fl6 = &fl->u.ip6; 2028c2ecf20Sopenharmony_ci struct ipv6_destopt_hao *hao = NULL; 2038c2ecf20Sopenharmony_ci struct xfrm_selector sel; 2048c2ecf20Sopenharmony_ci int offset; 2058c2ecf20Sopenharmony_ci ktime_t stamp; 2068c2ecf20Sopenharmony_ci int err = 0; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (unlikely(fl6->flowi6_proto == IPPROTO_MH && 2098c2ecf20Sopenharmony_ci fl6->fl6_mh_type <= IP6_MH_TYPE_MAX)) 2108c2ecf20Sopenharmony_ci goto out; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (likely(opt->dsthao)) { 2138c2ecf20Sopenharmony_ci offset = ipv6_find_tlv(skb, opt->dsthao, IPV6_TLV_HAO); 2148c2ecf20Sopenharmony_ci if (likely(offset >= 0)) 2158c2ecf20Sopenharmony_ci hao = (struct ipv6_destopt_hao *) 2168c2ecf20Sopenharmony_ci (skb_network_header(skb) + offset); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci stamp = skb_get_ktime(skb); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (!mip6_report_rl_allow(stamp, &ipv6_hdr(skb)->daddr, 2228c2ecf20Sopenharmony_ci hao ? &hao->addr : &ipv6_hdr(skb)->saddr, 2238c2ecf20Sopenharmony_ci opt->iif)) 2248c2ecf20Sopenharmony_ci goto out; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci memset(&sel, 0, sizeof(sel)); 2278c2ecf20Sopenharmony_ci memcpy(&sel.daddr, (xfrm_address_t *)&ipv6_hdr(skb)->daddr, 2288c2ecf20Sopenharmony_ci sizeof(sel.daddr)); 2298c2ecf20Sopenharmony_ci sel.prefixlen_d = 128; 2308c2ecf20Sopenharmony_ci memcpy(&sel.saddr, (xfrm_address_t *)&ipv6_hdr(skb)->saddr, 2318c2ecf20Sopenharmony_ci sizeof(sel.saddr)); 2328c2ecf20Sopenharmony_ci sel.prefixlen_s = 128; 2338c2ecf20Sopenharmony_ci sel.family = AF_INET6; 2348c2ecf20Sopenharmony_ci sel.proto = fl6->flowi6_proto; 2358c2ecf20Sopenharmony_ci sel.dport = xfrm_flowi_dport(fl, &fl6->uli); 2368c2ecf20Sopenharmony_ci if (sel.dport) 2378c2ecf20Sopenharmony_ci sel.dport_mask = htons(~0); 2388c2ecf20Sopenharmony_ci sel.sport = xfrm_flowi_sport(fl, &fl6->uli); 2398c2ecf20Sopenharmony_ci if (sel.sport) 2408c2ecf20Sopenharmony_ci sel.sport_mask = htons(~0); 2418c2ecf20Sopenharmony_ci sel.ifindex = fl6->flowi6_oif; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci err = km_report(net, IPPROTO_DSTOPTS, &sel, 2448c2ecf20Sopenharmony_ci (hao ? (xfrm_address_t *)&hao->addr : NULL)); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci out: 2478c2ecf20Sopenharmony_ci return err; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int mip6_destopt_offset(struct xfrm_state *x, struct sk_buff *skb, 2518c2ecf20Sopenharmony_ci u8 **nexthdr) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci u16 offset = sizeof(struct ipv6hdr); 2548c2ecf20Sopenharmony_ci struct ipv6_opt_hdr *exthdr = 2558c2ecf20Sopenharmony_ci (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1); 2568c2ecf20Sopenharmony_ci const unsigned char *nh = skb_network_header(skb); 2578c2ecf20Sopenharmony_ci unsigned int packet_len = skb_tail_pointer(skb) - 2588c2ecf20Sopenharmony_ci skb_network_header(skb); 2598c2ecf20Sopenharmony_ci int found_rhdr = 0; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci *nexthdr = &ipv6_hdr(skb)->nexthdr; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci while (offset + 1 <= packet_len) { 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci switch (**nexthdr) { 2668c2ecf20Sopenharmony_ci case NEXTHDR_HOP: 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci case NEXTHDR_ROUTING: 2698c2ecf20Sopenharmony_ci found_rhdr = 1; 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case NEXTHDR_DEST: 2728c2ecf20Sopenharmony_ci /* 2738c2ecf20Sopenharmony_ci * HAO MUST NOT appear more than once. 2748c2ecf20Sopenharmony_ci * XXX: It is better to try to find by the end of 2758c2ecf20Sopenharmony_ci * XXX: packet if HAO exists. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) { 2788c2ecf20Sopenharmony_ci net_dbg_ratelimited("mip6: hao exists already, override\n"); 2798c2ecf20Sopenharmony_ci return offset; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (found_rhdr) 2838c2ecf20Sopenharmony_ci return offset; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci default: 2878c2ecf20Sopenharmony_ci return offset; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci offset += ipv6_optlen(exthdr); 2918c2ecf20Sopenharmony_ci *nexthdr = &exthdr->nexthdr; 2928c2ecf20Sopenharmony_ci exthdr = (struct ipv6_opt_hdr *)(nh + offset); 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return offset; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int mip6_destopt_init_state(struct xfrm_state *x) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci if (x->id.spi) { 3018c2ecf20Sopenharmony_ci pr_info("%s: spi is not 0: %u\n", __func__, x->id.spi); 3028c2ecf20Sopenharmony_ci return -EINVAL; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci if (x->props.mode != XFRM_MODE_ROUTEOPTIMIZATION) { 3058c2ecf20Sopenharmony_ci pr_info("%s: state's mode is not %u: %u\n", 3068c2ecf20Sopenharmony_ci __func__, XFRM_MODE_ROUTEOPTIMIZATION, x->props.mode); 3078c2ecf20Sopenharmony_ci return -EINVAL; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci x->props.header_len = sizeof(struct ipv6_destopt_hdr) + 3118c2ecf20Sopenharmony_ci calc_padlen(sizeof(struct ipv6_destopt_hdr), 6) + 3128c2ecf20Sopenharmony_ci sizeof(struct ipv6_destopt_hao); 3138c2ecf20Sopenharmony_ci WARN_ON(x->props.header_len != 24); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/* 3198c2ecf20Sopenharmony_ci * Do nothing about destroying since it has no specific operation for 3208c2ecf20Sopenharmony_ci * destination options header unlike IPsec protocols. 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_cistatic void mip6_destopt_destroy(struct xfrm_state *x) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic const struct xfrm_type mip6_destopt_type = { 3278c2ecf20Sopenharmony_ci .description = "MIP6DESTOPT", 3288c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3298c2ecf20Sopenharmony_ci .proto = IPPROTO_DSTOPTS, 3308c2ecf20Sopenharmony_ci .flags = XFRM_TYPE_NON_FRAGMENT | XFRM_TYPE_LOCAL_COADDR, 3318c2ecf20Sopenharmony_ci .init_state = mip6_destopt_init_state, 3328c2ecf20Sopenharmony_ci .destructor = mip6_destopt_destroy, 3338c2ecf20Sopenharmony_ci .input = mip6_destopt_input, 3348c2ecf20Sopenharmony_ci .output = mip6_destopt_output, 3358c2ecf20Sopenharmony_ci .reject = mip6_destopt_reject, 3368c2ecf20Sopenharmony_ci .hdr_offset = mip6_destopt_offset, 3378c2ecf20Sopenharmony_ci}; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic int mip6_rthdr_input(struct xfrm_state *x, struct sk_buff *skb) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci const struct ipv6hdr *iph = ipv6_hdr(skb); 3428c2ecf20Sopenharmony_ci struct rt2_hdr *rt2 = (struct rt2_hdr *)skb->data; 3438c2ecf20Sopenharmony_ci int err = rt2->rt_hdr.nexthdr; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci spin_lock(&x->lock); 3468c2ecf20Sopenharmony_ci if (!ipv6_addr_equal(&iph->daddr, (struct in6_addr *)x->coaddr) && 3478c2ecf20Sopenharmony_ci !ipv6_addr_any((struct in6_addr *)x->coaddr)) 3488c2ecf20Sopenharmony_ci err = -ENOENT; 3498c2ecf20Sopenharmony_ci spin_unlock(&x->lock); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return err; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/* Routing Header type 2 is inserted. 3558c2ecf20Sopenharmony_ci * IP Header's dst address is replaced with Routing Header's Home Address. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_cistatic int mip6_rthdr_output(struct xfrm_state *x, struct sk_buff *skb) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct ipv6hdr *iph; 3608c2ecf20Sopenharmony_ci struct rt2_hdr *rt2; 3618c2ecf20Sopenharmony_ci u8 nexthdr; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci skb_push(skb, -skb_network_offset(skb)); 3648c2ecf20Sopenharmony_ci iph = ipv6_hdr(skb); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci nexthdr = *skb_mac_header(skb); 3678c2ecf20Sopenharmony_ci *skb_mac_header(skb) = IPPROTO_ROUTING; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci rt2 = (struct rt2_hdr *)skb_transport_header(skb); 3708c2ecf20Sopenharmony_ci rt2->rt_hdr.nexthdr = nexthdr; 3718c2ecf20Sopenharmony_ci rt2->rt_hdr.hdrlen = (x->props.header_len >> 3) - 1; 3728c2ecf20Sopenharmony_ci rt2->rt_hdr.type = IPV6_SRCRT_TYPE_2; 3738c2ecf20Sopenharmony_ci rt2->rt_hdr.segments_left = 1; 3748c2ecf20Sopenharmony_ci memset(&rt2->reserved, 0, sizeof(rt2->reserved)); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci WARN_ON(rt2->rt_hdr.hdrlen != 2); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci memcpy(&rt2->addr, &iph->daddr, sizeof(rt2->addr)); 3798c2ecf20Sopenharmony_ci spin_lock_bh(&x->lock); 3808c2ecf20Sopenharmony_ci memcpy(&iph->daddr, x->coaddr, sizeof(iph->daddr)); 3818c2ecf20Sopenharmony_ci spin_unlock_bh(&x->lock); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int mip6_rthdr_offset(struct xfrm_state *x, struct sk_buff *skb, 3878c2ecf20Sopenharmony_ci u8 **nexthdr) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci u16 offset = sizeof(struct ipv6hdr); 3908c2ecf20Sopenharmony_ci struct ipv6_opt_hdr *exthdr = 3918c2ecf20Sopenharmony_ci (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1); 3928c2ecf20Sopenharmony_ci const unsigned char *nh = skb_network_header(skb); 3938c2ecf20Sopenharmony_ci unsigned int packet_len = skb_tail_pointer(skb) - 3948c2ecf20Sopenharmony_ci skb_network_header(skb); 3958c2ecf20Sopenharmony_ci int found_rhdr = 0; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci *nexthdr = &ipv6_hdr(skb)->nexthdr; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci while (offset + 1 <= packet_len) { 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci switch (**nexthdr) { 4028c2ecf20Sopenharmony_ci case NEXTHDR_HOP: 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci case NEXTHDR_ROUTING: 4058c2ecf20Sopenharmony_ci if (offset + 3 <= packet_len) { 4068c2ecf20Sopenharmony_ci struct ipv6_rt_hdr *rt; 4078c2ecf20Sopenharmony_ci rt = (struct ipv6_rt_hdr *)(nh + offset); 4088c2ecf20Sopenharmony_ci if (rt->type != 0) 4098c2ecf20Sopenharmony_ci return offset; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci found_rhdr = 1; 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci case NEXTHDR_DEST: 4148c2ecf20Sopenharmony_ci if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) 4158c2ecf20Sopenharmony_ci return offset; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (found_rhdr) 4188c2ecf20Sopenharmony_ci return offset; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci default: 4228c2ecf20Sopenharmony_ci return offset; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci offset += ipv6_optlen(exthdr); 4268c2ecf20Sopenharmony_ci *nexthdr = &exthdr->nexthdr; 4278c2ecf20Sopenharmony_ci exthdr = (struct ipv6_opt_hdr *)(nh + offset); 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci return offset; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic int mip6_rthdr_init_state(struct xfrm_state *x) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci if (x->id.spi) { 4368c2ecf20Sopenharmony_ci pr_info("%s: spi is not 0: %u\n", __func__, x->id.spi); 4378c2ecf20Sopenharmony_ci return -EINVAL; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci if (x->props.mode != XFRM_MODE_ROUTEOPTIMIZATION) { 4408c2ecf20Sopenharmony_ci pr_info("%s: state's mode is not %u: %u\n", 4418c2ecf20Sopenharmony_ci __func__, XFRM_MODE_ROUTEOPTIMIZATION, x->props.mode); 4428c2ecf20Sopenharmony_ci return -EINVAL; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci x->props.header_len = sizeof(struct rt2_hdr); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return 0; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci/* 4518c2ecf20Sopenharmony_ci * Do nothing about destroying since it has no specific operation for routing 4528c2ecf20Sopenharmony_ci * header type 2 unlike IPsec protocols. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistatic void mip6_rthdr_destroy(struct xfrm_state *x) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic const struct xfrm_type mip6_rthdr_type = { 4598c2ecf20Sopenharmony_ci .description = "MIP6RT", 4608c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4618c2ecf20Sopenharmony_ci .proto = IPPROTO_ROUTING, 4628c2ecf20Sopenharmony_ci .flags = XFRM_TYPE_NON_FRAGMENT | XFRM_TYPE_REMOTE_COADDR, 4638c2ecf20Sopenharmony_ci .init_state = mip6_rthdr_init_state, 4648c2ecf20Sopenharmony_ci .destructor = mip6_rthdr_destroy, 4658c2ecf20Sopenharmony_ci .input = mip6_rthdr_input, 4668c2ecf20Sopenharmony_ci .output = mip6_rthdr_output, 4678c2ecf20Sopenharmony_ci .hdr_offset = mip6_rthdr_offset, 4688c2ecf20Sopenharmony_ci}; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int __init mip6_init(void) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci pr_info("Mobile IPv6\n"); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (xfrm_register_type(&mip6_destopt_type, AF_INET6) < 0) { 4758c2ecf20Sopenharmony_ci pr_info("%s: can't add xfrm type(destopt)\n", __func__); 4768c2ecf20Sopenharmony_ci goto mip6_destopt_xfrm_fail; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci if (xfrm_register_type(&mip6_rthdr_type, AF_INET6) < 0) { 4798c2ecf20Sopenharmony_ci pr_info("%s: can't add xfrm type(rthdr)\n", __func__); 4808c2ecf20Sopenharmony_ci goto mip6_rthdr_xfrm_fail; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci if (rawv6_mh_filter_register(mip6_mh_filter) < 0) { 4838c2ecf20Sopenharmony_ci pr_info("%s: can't add rawv6 mh filter\n", __func__); 4848c2ecf20Sopenharmony_ci goto mip6_rawv6_mh_fail; 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci mip6_rawv6_mh_fail: 4918c2ecf20Sopenharmony_ci xfrm_unregister_type(&mip6_rthdr_type, AF_INET6); 4928c2ecf20Sopenharmony_ci mip6_rthdr_xfrm_fail: 4938c2ecf20Sopenharmony_ci xfrm_unregister_type(&mip6_destopt_type, AF_INET6); 4948c2ecf20Sopenharmony_ci mip6_destopt_xfrm_fail: 4958c2ecf20Sopenharmony_ci return -EAGAIN; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic void __exit mip6_fini(void) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci if (rawv6_mh_filter_unregister(mip6_mh_filter) < 0) 5018c2ecf20Sopenharmony_ci pr_info("%s: can't remove rawv6 mh filter\n", __func__); 5028c2ecf20Sopenharmony_ci xfrm_unregister_type(&mip6_rthdr_type, AF_INET6); 5038c2ecf20Sopenharmony_ci xfrm_unregister_type(&mip6_destopt_type, AF_INET6); 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cimodule_init(mip6_init); 5078c2ecf20Sopenharmony_cimodule_exit(mip6_fini); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5108c2ecf20Sopenharmony_ciMODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_DSTOPTS); 5118c2ecf20Sopenharmony_ciMODULE_ALIAS_XFRM_TYPE(AF_INET6, XFRM_PROTO_ROUTING); 512