18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * RAW sockets for IPv6 48c2ecf20Sopenharmony_ci * Linux INET6 implementation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Adapted from linux/net/ipv4/raw.c 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Fixes: 128c2ecf20Sopenharmony_ci * Hideaki YOSHIFUJI : sin6_scope_id support 138c2ecf20Sopenharmony_ci * YOSHIFUJI,H.@USAGI : raw checksum (RFC2292(bis) compliance) 148c2ecf20Sopenharmony_ci * Kazunori MIYAZAWA @USAGI: change process style to use ip6_append_data 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/errno.h> 188c2ecf20Sopenharmony_ci#include <linux/types.h> 198c2ecf20Sopenharmony_ci#include <linux/socket.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/sockios.h> 228c2ecf20Sopenharmony_ci#include <linux/net.h> 238c2ecf20Sopenharmony_ci#include <linux/in6.h> 248c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 258c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 268c2ecf20Sopenharmony_ci#include <linux/icmpv6.h> 278c2ecf20Sopenharmony_ci#include <linux/netfilter.h> 288c2ecf20Sopenharmony_ci#include <linux/netfilter_ipv6.h> 298c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 308c2ecf20Sopenharmony_ci#include <linux/compat.h> 318c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 328c2ecf20Sopenharmony_ci#include <asm/ioctls.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <net/net_namespace.h> 358c2ecf20Sopenharmony_ci#include <net/ip.h> 368c2ecf20Sopenharmony_ci#include <net/sock.h> 378c2ecf20Sopenharmony_ci#include <net/snmp.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include <net/ipv6.h> 408c2ecf20Sopenharmony_ci#include <net/ndisc.h> 418c2ecf20Sopenharmony_ci#include <net/protocol.h> 428c2ecf20Sopenharmony_ci#include <net/ip6_route.h> 438c2ecf20Sopenharmony_ci#include <net/ip6_checksum.h> 448c2ecf20Sopenharmony_ci#include <net/addrconf.h> 458c2ecf20Sopenharmony_ci#include <net/transp_v6.h> 468c2ecf20Sopenharmony_ci#include <net/udp.h> 478c2ecf20Sopenharmony_ci#include <net/inet_common.h> 488c2ecf20Sopenharmony_ci#include <net/tcp_states.h> 498c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 508c2ecf20Sopenharmony_ci#include <net/mip6.h> 518c2ecf20Sopenharmony_ci#endif 528c2ecf20Sopenharmony_ci#include <linux/mroute6.h> 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#include <net/raw.h> 558c2ecf20Sopenharmony_ci#include <net/rawv6.h> 568c2ecf20Sopenharmony_ci#include <net/xfrm.h> 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 598c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 608c2ecf20Sopenharmony_ci#include <linux/export.h> 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define ICMPV6_HDRLEN 4 /* ICMPv6 header, RFC 4443 Section 2.1 */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct raw_hashinfo raw_v6_hashinfo = { 658c2ecf20Sopenharmony_ci .lock = __RW_LOCK_UNLOCKED(raw_v6_hashinfo.lock), 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(raw_v6_hashinfo); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct sock *__raw_v6_lookup(struct net *net, struct sock *sk, 708c2ecf20Sopenharmony_ci unsigned short num, const struct in6_addr *loc_addr, 718c2ecf20Sopenharmony_ci const struct in6_addr *rmt_addr, int dif, int sdif) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci bool is_multicast = ipv6_addr_is_multicast(loc_addr); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci sk_for_each_from(sk) 768c2ecf20Sopenharmony_ci if (inet_sk(sk)->inet_num == num) { 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 798c2ecf20Sopenharmony_ci continue; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&sk->sk_v6_daddr) && 828c2ecf20Sopenharmony_ci !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr)) 838c2ecf20Sopenharmony_ci continue; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (!raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if, 868c2ecf20Sopenharmony_ci dif, sdif)) 878c2ecf20Sopenharmony_ci continue; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { 908c2ecf20Sopenharmony_ci if (ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr)) 918c2ecf20Sopenharmony_ci goto found; 928c2ecf20Sopenharmony_ci if (is_multicast && 938c2ecf20Sopenharmony_ci inet6_mc_check(sk, loc_addr, rmt_addr)) 948c2ecf20Sopenharmony_ci goto found; 958c2ecf20Sopenharmony_ci continue; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci goto found; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci sk = NULL; 1008c2ecf20Sopenharmony_cifound: 1018c2ecf20Sopenharmony_ci return sk; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__raw_v6_lookup); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* 1068c2ecf20Sopenharmony_ci * 0 - deliver 1078c2ecf20Sopenharmony_ci * 1 - block 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_cistatic int icmpv6_filter(const struct sock *sk, const struct sk_buff *skb) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct icmp6hdr _hdr; 1128c2ecf20Sopenharmony_ci const struct icmp6hdr *hdr; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* We require only the four bytes of the ICMPv6 header, not any 1158c2ecf20Sopenharmony_ci * additional bytes of message body in "struct icmp6hdr". 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci hdr = skb_header_pointer(skb, skb_transport_offset(skb), 1188c2ecf20Sopenharmony_ci ICMPV6_HDRLEN, &_hdr); 1198c2ecf20Sopenharmony_ci if (hdr) { 1208c2ecf20Sopenharmony_ci const __u32 *data = &raw6_sk(sk)->filter.data[0]; 1218c2ecf20Sopenharmony_ci unsigned int type = hdr->icmp6_type; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return (data[type >> 5] & (1U << (type & 31))) != 0; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci return 1; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 1298c2ecf20Sopenharmony_citypedef int mh_filter_t(struct sock *sock, struct sk_buff *skb); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic mh_filter_t __rcu *mh_filter __read_mostly; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ciint rawv6_mh_filter_register(mh_filter_t filter) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci rcu_assign_pointer(mh_filter, filter); 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rawv6_mh_filter_register); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ciint rawv6_mh_filter_unregister(mh_filter_t filter) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci RCU_INIT_POINTER(mh_filter, NULL); 1438c2ecf20Sopenharmony_ci synchronize_rcu(); 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rawv6_mh_filter_unregister); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci#endif 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/* 1518c2ecf20Sopenharmony_ci * demultiplex raw sockets. 1528c2ecf20Sopenharmony_ci * (should consider queueing the skb in the sock receive_queue 1538c2ecf20Sopenharmony_ci * without calling rawv6.c) 1548c2ecf20Sopenharmony_ci * 1558c2ecf20Sopenharmony_ci * Caller owns SKB so we must make clones. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_cistatic bool ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci const struct in6_addr *saddr; 1608c2ecf20Sopenharmony_ci const struct in6_addr *daddr; 1618c2ecf20Sopenharmony_ci struct sock *sk; 1628c2ecf20Sopenharmony_ci bool delivered = false; 1638c2ecf20Sopenharmony_ci __u8 hash; 1648c2ecf20Sopenharmony_ci struct net *net; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci saddr = &ipv6_hdr(skb)->saddr; 1678c2ecf20Sopenharmony_ci daddr = saddr + 1; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci hash = nexthdr & (RAW_HTABLE_SIZE - 1); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci read_lock(&raw_v6_hashinfo.lock); 1728c2ecf20Sopenharmony_ci sk = sk_head(&raw_v6_hashinfo.ht[hash]); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (!sk) 1758c2ecf20Sopenharmony_ci goto out; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci net = dev_net(skb->dev); 1788c2ecf20Sopenharmony_ci sk = __raw_v6_lookup(net, sk, nexthdr, daddr, saddr, 1798c2ecf20Sopenharmony_ci inet6_iif(skb), inet6_sdif(skb)); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci while (sk) { 1828c2ecf20Sopenharmony_ci int filtered; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci delivered = true; 1858c2ecf20Sopenharmony_ci switch (nexthdr) { 1868c2ecf20Sopenharmony_ci case IPPROTO_ICMPV6: 1878c2ecf20Sopenharmony_ci filtered = icmpv6_filter(sk, skb); 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 1918c2ecf20Sopenharmony_ci case IPPROTO_MH: 1928c2ecf20Sopenharmony_ci { 1938c2ecf20Sopenharmony_ci /* XXX: To validate MH only once for each packet, 1948c2ecf20Sopenharmony_ci * this is placed here. It should be after checking 1958c2ecf20Sopenharmony_ci * xfrm policy, however it doesn't. The checking xfrm 1968c2ecf20Sopenharmony_ci * policy is placed in rawv6_rcv() because it is 1978c2ecf20Sopenharmony_ci * required for each socket. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ci mh_filter_t *filter; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci filter = rcu_dereference(mh_filter); 2028c2ecf20Sopenharmony_ci filtered = filter ? (*filter)(sk, skb) : 0; 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci#endif 2068c2ecf20Sopenharmony_ci default: 2078c2ecf20Sopenharmony_ci filtered = 0; 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (filtered < 0) 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci if (filtered == 0) { 2148c2ecf20Sopenharmony_ci struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* Not releasing hash table! */ 2178c2ecf20Sopenharmony_ci if (clone) { 2188c2ecf20Sopenharmony_ci nf_reset_ct(clone); 2198c2ecf20Sopenharmony_ci rawv6_rcv(sk, clone); 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci sk = __raw_v6_lookup(net, sk_next(sk), nexthdr, daddr, saddr, 2238c2ecf20Sopenharmony_ci inet6_iif(skb), inet6_sdif(skb)); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ciout: 2268c2ecf20Sopenharmony_ci read_unlock(&raw_v6_hashinfo.lock); 2278c2ecf20Sopenharmony_ci return delivered; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cibool raw6_local_deliver(struct sk_buff *skb, int nexthdr) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct sock *raw_sk; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci raw_sk = sk_head(&raw_v6_hashinfo.ht[nexthdr & (RAW_HTABLE_SIZE - 1)]); 2358c2ecf20Sopenharmony_ci if (raw_sk && !ipv6_raw_deliver(skb, nexthdr)) 2368c2ecf20Sopenharmony_ci raw_sk = NULL; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return raw_sk != NULL; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/* This cleans up af_inet6 a bit. -DaveM */ 2428c2ecf20Sopenharmony_cistatic int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 2458c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 2468c2ecf20Sopenharmony_ci struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr; 2478c2ecf20Sopenharmony_ci __be32 v4addr = 0; 2488c2ecf20Sopenharmony_ci int addr_type; 2498c2ecf20Sopenharmony_ci int err; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (addr_len < SIN6_LEN_RFC2133) 2528c2ecf20Sopenharmony_ci return -EINVAL; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (addr->sin6_family != AF_INET6) 2558c2ecf20Sopenharmony_ci return -EINVAL; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci addr_type = ipv6_addr_type(&addr->sin6_addr); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* Raw sockets are IPv6 only */ 2608c2ecf20Sopenharmony_ci if (addr_type == IPV6_ADDR_MAPPED) 2618c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci lock_sock(sk); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci err = -EINVAL; 2668c2ecf20Sopenharmony_ci if (sk->sk_state != TCP_CLOSE) 2678c2ecf20Sopenharmony_ci goto out; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci rcu_read_lock(); 2708c2ecf20Sopenharmony_ci /* Check if the address belongs to the host. */ 2718c2ecf20Sopenharmony_ci if (addr_type != IPV6_ADDR_ANY) { 2728c2ecf20Sopenharmony_ci struct net_device *dev = NULL; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (__ipv6_addr_needs_scope_id(addr_type)) { 2758c2ecf20Sopenharmony_ci if (addr_len >= sizeof(struct sockaddr_in6) && 2768c2ecf20Sopenharmony_ci addr->sin6_scope_id) { 2778c2ecf20Sopenharmony_ci /* Override any existing binding, if another 2788c2ecf20Sopenharmony_ci * one is supplied by user. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci sk->sk_bound_dev_if = addr->sin6_scope_id; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* Binding to link-local address requires an interface */ 2848c2ecf20Sopenharmony_ci if (!sk->sk_bound_dev_if) 2858c2ecf20Sopenharmony_ci goto out_unlock; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (sk->sk_bound_dev_if) { 2898c2ecf20Sopenharmony_ci err = -ENODEV; 2908c2ecf20Sopenharmony_ci dev = dev_get_by_index_rcu(sock_net(sk), 2918c2ecf20Sopenharmony_ci sk->sk_bound_dev_if); 2928c2ecf20Sopenharmony_ci if (!dev) 2938c2ecf20Sopenharmony_ci goto out_unlock; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* ipv4 addr of the socket is invalid. Only the 2978c2ecf20Sopenharmony_ci * unspecified and mapped address have a v4 equivalent. 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_ci v4addr = LOOPBACK4_IPV6; 3008c2ecf20Sopenharmony_ci if (!(addr_type & IPV6_ADDR_MULTICAST) && 3018c2ecf20Sopenharmony_ci !ipv6_can_nonlocal_bind(sock_net(sk), inet)) { 3028c2ecf20Sopenharmony_ci err = -EADDRNOTAVAIL; 3038c2ecf20Sopenharmony_ci if (!ipv6_chk_addr(sock_net(sk), &addr->sin6_addr, 3048c2ecf20Sopenharmony_ci dev, 0)) { 3058c2ecf20Sopenharmony_ci goto out_unlock; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci inet->inet_rcv_saddr = inet->inet_saddr = v4addr; 3118c2ecf20Sopenharmony_ci sk->sk_v6_rcv_saddr = addr->sin6_addr; 3128c2ecf20Sopenharmony_ci if (!(addr_type & IPV6_ADDR_MULTICAST)) 3138c2ecf20Sopenharmony_ci np->saddr = addr->sin6_addr; 3148c2ecf20Sopenharmony_ci err = 0; 3158c2ecf20Sopenharmony_ciout_unlock: 3168c2ecf20Sopenharmony_ci rcu_read_unlock(); 3178c2ecf20Sopenharmony_ciout: 3188c2ecf20Sopenharmony_ci release_sock(sk); 3198c2ecf20Sopenharmony_ci return err; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void rawv6_err(struct sock *sk, struct sk_buff *skb, 3238c2ecf20Sopenharmony_ci struct inet6_skb_parm *opt, 3248c2ecf20Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 3278c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 3288c2ecf20Sopenharmony_ci int err; 3298c2ecf20Sopenharmony_ci int harderr; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* Report error on raw socket, if: 3328c2ecf20Sopenharmony_ci 1. User requested recverr. 3338c2ecf20Sopenharmony_ci 2. Socket is connected (otherwise the error indication 3348c2ecf20Sopenharmony_ci is useless without recverr and error is hard. 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ci if (!np->recverr && sk->sk_state != TCP_ESTABLISHED) 3378c2ecf20Sopenharmony_ci return; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci harderr = icmpv6_err_convert(type, code, &err); 3408c2ecf20Sopenharmony_ci if (type == ICMPV6_PKT_TOOBIG) { 3418c2ecf20Sopenharmony_ci ip6_sk_update_pmtu(skb, sk, info); 3428c2ecf20Sopenharmony_ci harderr = (np->pmtudisc == IPV6_PMTUDISC_DO); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci if (type == NDISC_REDIRECT) { 3458c2ecf20Sopenharmony_ci ip6_sk_redirect(skb, sk); 3468c2ecf20Sopenharmony_ci return; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci if (np->recverr) { 3498c2ecf20Sopenharmony_ci u8 *payload = skb->data; 3508c2ecf20Sopenharmony_ci if (!inet->hdrincl) 3518c2ecf20Sopenharmony_ci payload += offset; 3528c2ecf20Sopenharmony_ci ipv6_icmp_error(sk, skb, err, 0, ntohl(info), payload); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (np->recverr || harderr) { 3568c2ecf20Sopenharmony_ci sk->sk_err = err; 3578c2ecf20Sopenharmony_ci sk->sk_error_report(sk); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_civoid raw6_icmp_error(struct sk_buff *skb, int nexthdr, 3628c2ecf20Sopenharmony_ci u8 type, u8 code, int inner_offset, __be32 info) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct sock *sk; 3658c2ecf20Sopenharmony_ci int hash; 3668c2ecf20Sopenharmony_ci const struct in6_addr *saddr, *daddr; 3678c2ecf20Sopenharmony_ci struct net *net; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci hash = nexthdr & (RAW_HTABLE_SIZE - 1); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci read_lock(&raw_v6_hashinfo.lock); 3728c2ecf20Sopenharmony_ci sk = sk_head(&raw_v6_hashinfo.ht[hash]); 3738c2ecf20Sopenharmony_ci if (sk) { 3748c2ecf20Sopenharmony_ci /* Note: ipv6_hdr(skb) != skb->data */ 3758c2ecf20Sopenharmony_ci const struct ipv6hdr *ip6h = (const struct ipv6hdr *)skb->data; 3768c2ecf20Sopenharmony_ci saddr = &ip6h->saddr; 3778c2ecf20Sopenharmony_ci daddr = &ip6h->daddr; 3788c2ecf20Sopenharmony_ci net = dev_net(skb->dev); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci while ((sk = __raw_v6_lookup(net, sk, nexthdr, saddr, daddr, 3818c2ecf20Sopenharmony_ci inet6_iif(skb), inet6_iif(skb)))) { 3828c2ecf20Sopenharmony_ci rawv6_err(sk, skb, NULL, type, code, 3838c2ecf20Sopenharmony_ci inner_offset, info); 3848c2ecf20Sopenharmony_ci sk = sk_next(sk); 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci read_unlock(&raw_v6_hashinfo.lock); 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic inline int rawv6_rcv_skb(struct sock *sk, struct sk_buff *skb) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci if ((raw6_sk(sk)->checksum || rcu_access_pointer(sk->sk_filter)) && 3938c2ecf20Sopenharmony_ci skb_checksum_complete(skb)) { 3948c2ecf20Sopenharmony_ci atomic_inc(&sk->sk_drops); 3958c2ecf20Sopenharmony_ci kfree_skb(skb); 3968c2ecf20Sopenharmony_ci return NET_RX_DROP; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* Charge it to the socket. */ 4008c2ecf20Sopenharmony_ci skb_dst_drop(skb); 4018c2ecf20Sopenharmony_ci if (sock_queue_rcv_skb(sk, skb) < 0) { 4028c2ecf20Sopenharmony_ci kfree_skb(skb); 4038c2ecf20Sopenharmony_ci return NET_RX_DROP; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/* 4108c2ecf20Sopenharmony_ci * This is next to useless... 4118c2ecf20Sopenharmony_ci * if we demultiplex in network layer we don't need the extra call 4128c2ecf20Sopenharmony_ci * just to queue the skb... 4138c2ecf20Sopenharmony_ci * maybe we could have the network decide upon a hint if it 4148c2ecf20Sopenharmony_ci * should call raw_rcv for demultiplexing 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_ciint rawv6_rcv(struct sock *sk, struct sk_buff *skb) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 4198c2ecf20Sopenharmony_ci struct raw6_sock *rp = raw6_sk(sk); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) { 4228c2ecf20Sopenharmony_ci atomic_inc(&sk->sk_drops); 4238c2ecf20Sopenharmony_ci kfree_skb(skb); 4248c2ecf20Sopenharmony_ci return NET_RX_DROP; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (!rp->checksum) 4288c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_COMPLETE) { 4318c2ecf20Sopenharmony_ci skb_postpull_rcsum(skb, skb_network_header(skb), 4328c2ecf20Sopenharmony_ci skb_network_header_len(skb)); 4338c2ecf20Sopenharmony_ci if (!csum_ipv6_magic(&ipv6_hdr(skb)->saddr, 4348c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->daddr, 4358c2ecf20Sopenharmony_ci skb->len, inet->inet_num, skb->csum)) 4368c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci if (!skb_csum_unnecessary(skb)) 4398c2ecf20Sopenharmony_ci skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr, 4408c2ecf20Sopenharmony_ci &ipv6_hdr(skb)->daddr, 4418c2ecf20Sopenharmony_ci skb->len, 4428c2ecf20Sopenharmony_ci inet->inet_num, 0)); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (inet->hdrincl) { 4458c2ecf20Sopenharmony_ci if (skb_checksum_complete(skb)) { 4468c2ecf20Sopenharmony_ci atomic_inc(&sk->sk_drops); 4478c2ecf20Sopenharmony_ci kfree_skb(skb); 4488c2ecf20Sopenharmony_ci return NET_RX_DROP; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci rawv6_rcv_skb(sk, skb); 4538c2ecf20Sopenharmony_ci return 0; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci/* 4588c2ecf20Sopenharmony_ci * This should be easy, if there is something there 4598c2ecf20Sopenharmony_ci * we return it, otherwise we block. 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, 4638c2ecf20Sopenharmony_ci int noblock, int flags, int *addr_len) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 4668c2ecf20Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); 4678c2ecf20Sopenharmony_ci struct sk_buff *skb; 4688c2ecf20Sopenharmony_ci size_t copied; 4698c2ecf20Sopenharmony_ci int err; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (flags & MSG_OOB) 4728c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (flags & MSG_ERRQUEUE) 4758c2ecf20Sopenharmony_ci return ipv6_recv_error(sk, msg, len, addr_len); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (np->rxpmtu && np->rxopt.bits.rxpmtu) 4788c2ecf20Sopenharmony_ci return ipv6_recv_rxpmtu(sk, msg, len, addr_len); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci skb = skb_recv_datagram(sk, flags, noblock, &err); 4818c2ecf20Sopenharmony_ci if (!skb) 4828c2ecf20Sopenharmony_ci goto out; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci copied = skb->len; 4858c2ecf20Sopenharmony_ci if (copied > len) { 4868c2ecf20Sopenharmony_ci copied = len; 4878c2ecf20Sopenharmony_ci msg->msg_flags |= MSG_TRUNC; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (skb_csum_unnecessary(skb)) { 4918c2ecf20Sopenharmony_ci err = skb_copy_datagram_msg(skb, 0, msg, copied); 4928c2ecf20Sopenharmony_ci } else if (msg->msg_flags&MSG_TRUNC) { 4938c2ecf20Sopenharmony_ci if (__skb_checksum_complete(skb)) 4948c2ecf20Sopenharmony_ci goto csum_copy_err; 4958c2ecf20Sopenharmony_ci err = skb_copy_datagram_msg(skb, 0, msg, copied); 4968c2ecf20Sopenharmony_ci } else { 4978c2ecf20Sopenharmony_ci err = skb_copy_and_csum_datagram_msg(skb, 0, msg); 4988c2ecf20Sopenharmony_ci if (err == -EINVAL) 4998c2ecf20Sopenharmony_ci goto csum_copy_err; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci if (err) 5028c2ecf20Sopenharmony_ci goto out_free; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci /* Copy the address. */ 5058c2ecf20Sopenharmony_ci if (sin6) { 5068c2ecf20Sopenharmony_ci sin6->sin6_family = AF_INET6; 5078c2ecf20Sopenharmony_ci sin6->sin6_port = 0; 5088c2ecf20Sopenharmony_ci sin6->sin6_addr = ipv6_hdr(skb)->saddr; 5098c2ecf20Sopenharmony_ci sin6->sin6_flowinfo = 0; 5108c2ecf20Sopenharmony_ci sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr, 5118c2ecf20Sopenharmony_ci inet6_iif(skb)); 5128c2ecf20Sopenharmony_ci *addr_len = sizeof(*sin6); 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci sock_recv_ts_and_drops(msg, sk, skb); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (np->rxopt.all) 5188c2ecf20Sopenharmony_ci ip6_datagram_recv_ctl(sk, msg, skb); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci err = copied; 5218c2ecf20Sopenharmony_ci if (flags & MSG_TRUNC) 5228c2ecf20Sopenharmony_ci err = skb->len; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ciout_free: 5258c2ecf20Sopenharmony_ci skb_free_datagram(sk, skb); 5268c2ecf20Sopenharmony_ciout: 5278c2ecf20Sopenharmony_ci return err; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cicsum_copy_err: 5308c2ecf20Sopenharmony_ci skb_kill_datagram(sk, skb, flags); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* Error for blocking case is chosen to masquerade 5338c2ecf20Sopenharmony_ci as some normal condition. 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH; 5368c2ecf20Sopenharmony_ci goto out; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, 5408c2ecf20Sopenharmony_ci struct raw6_sock *rp) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct ipv6_txoptions *opt; 5438c2ecf20Sopenharmony_ci struct sk_buff *skb; 5448c2ecf20Sopenharmony_ci int err = 0; 5458c2ecf20Sopenharmony_ci int offset; 5468c2ecf20Sopenharmony_ci int len; 5478c2ecf20Sopenharmony_ci int total_len; 5488c2ecf20Sopenharmony_ci __wsum tmp_csum; 5498c2ecf20Sopenharmony_ci __sum16 csum; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (!rp->checksum) 5528c2ecf20Sopenharmony_ci goto send; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci skb = skb_peek(&sk->sk_write_queue); 5558c2ecf20Sopenharmony_ci if (!skb) 5568c2ecf20Sopenharmony_ci goto out; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci offset = rp->offset; 5598c2ecf20Sopenharmony_ci total_len = inet_sk(sk)->cork.base.length; 5608c2ecf20Sopenharmony_ci opt = inet6_sk(sk)->cork.opt; 5618c2ecf20Sopenharmony_ci total_len -= opt ? opt->opt_flen : 0; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (offset >= total_len - 1) { 5648c2ecf20Sopenharmony_ci err = -EINVAL; 5658c2ecf20Sopenharmony_ci ip6_flush_pending_frames(sk); 5668c2ecf20Sopenharmony_ci goto out; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* should be check HW csum miyazawa */ 5708c2ecf20Sopenharmony_ci if (skb_queue_len(&sk->sk_write_queue) == 1) { 5718c2ecf20Sopenharmony_ci /* 5728c2ecf20Sopenharmony_ci * Only one fragment on the socket. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_ci tmp_csum = skb->csum; 5758c2ecf20Sopenharmony_ci } else { 5768c2ecf20Sopenharmony_ci struct sk_buff *csum_skb = NULL; 5778c2ecf20Sopenharmony_ci tmp_csum = 0; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci skb_queue_walk(&sk->sk_write_queue, skb) { 5808c2ecf20Sopenharmony_ci tmp_csum = csum_add(tmp_csum, skb->csum); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci if (csum_skb) 5838c2ecf20Sopenharmony_ci continue; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci len = skb->len - skb_transport_offset(skb); 5868c2ecf20Sopenharmony_ci if (offset >= len) { 5878c2ecf20Sopenharmony_ci offset -= len; 5888c2ecf20Sopenharmony_ci continue; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci csum_skb = skb; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci skb = csum_skb; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci offset += skb_transport_offset(skb); 5988c2ecf20Sopenharmony_ci err = skb_copy_bits(skb, offset, &csum, 2); 5998c2ecf20Sopenharmony_ci if (err < 0) { 6008c2ecf20Sopenharmony_ci ip6_flush_pending_frames(sk); 6018c2ecf20Sopenharmony_ci goto out; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* in case cksum was not initialized */ 6058c2ecf20Sopenharmony_ci if (unlikely(csum)) 6068c2ecf20Sopenharmony_ci tmp_csum = csum_sub(tmp_csum, csum_unfold(csum)); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci csum = csum_ipv6_magic(&fl6->saddr, &fl6->daddr, 6098c2ecf20Sopenharmony_ci total_len, fl6->flowi6_proto, tmp_csum); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (csum == 0 && fl6->flowi6_proto == IPPROTO_UDP) 6128c2ecf20Sopenharmony_ci csum = CSUM_MANGLED_0; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci BUG_ON(skb_store_bits(skb, offset, &csum, 2)); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cisend: 6178c2ecf20Sopenharmony_ci err = ip6_push_pending_frames(sk); 6188c2ecf20Sopenharmony_ciout: 6198c2ecf20Sopenharmony_ci return err; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, 6238c2ecf20Sopenharmony_ci struct flowi6 *fl6, struct dst_entry **dstp, 6248c2ecf20Sopenharmony_ci unsigned int flags, const struct sockcm_cookie *sockc) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 6278c2ecf20Sopenharmony_ci struct net *net = sock_net(sk); 6288c2ecf20Sopenharmony_ci struct ipv6hdr *iph; 6298c2ecf20Sopenharmony_ci struct sk_buff *skb; 6308c2ecf20Sopenharmony_ci int err; 6318c2ecf20Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)*dstp; 6328c2ecf20Sopenharmony_ci int hlen = LL_RESERVED_SPACE(rt->dst.dev); 6338c2ecf20Sopenharmony_ci int tlen = rt->dst.dev->needed_tailroom; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (length > rt->dst.dev->mtu) { 6368c2ecf20Sopenharmony_ci ipv6_local_error(sk, EMSGSIZE, fl6, rt->dst.dev->mtu); 6378c2ecf20Sopenharmony_ci return -EMSGSIZE; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci if (length < sizeof(struct ipv6hdr)) 6408c2ecf20Sopenharmony_ci return -EINVAL; 6418c2ecf20Sopenharmony_ci if (flags&MSG_PROBE) 6428c2ecf20Sopenharmony_ci goto out; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci skb = sock_alloc_send_skb(sk, 6458c2ecf20Sopenharmony_ci length + hlen + tlen + 15, 6468c2ecf20Sopenharmony_ci flags & MSG_DONTWAIT, &err); 6478c2ecf20Sopenharmony_ci if (!skb) 6488c2ecf20Sopenharmony_ci goto error; 6498c2ecf20Sopenharmony_ci skb_reserve(skb, hlen); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 6528c2ecf20Sopenharmony_ci skb->priority = sk->sk_priority; 6538c2ecf20Sopenharmony_ci skb->mark = sockc->mark; 6548c2ecf20Sopenharmony_ci skb->tstamp = sockc->transmit_time; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci skb_put(skb, length); 6578c2ecf20Sopenharmony_ci skb_reset_network_header(skb); 6588c2ecf20Sopenharmony_ci iph = ipv6_hdr(skb); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci skb_setup_tx_timestamp(skb, sockc->tsflags); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (flags & MSG_CONFIRM) 6658c2ecf20Sopenharmony_ci skb_set_dst_pending_confirm(skb, 1); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci skb->transport_header = skb->network_header; 6688c2ecf20Sopenharmony_ci err = memcpy_from_msg(iph, msg, length); 6698c2ecf20Sopenharmony_ci if (err) { 6708c2ecf20Sopenharmony_ci err = -EFAULT; 6718c2ecf20Sopenharmony_ci kfree_skb(skb); 6728c2ecf20Sopenharmony_ci goto error; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci skb_dst_set(skb, &rt->dst); 6768c2ecf20Sopenharmony_ci *dstp = NULL; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* if egress device is enslaved to an L3 master device pass the 6798c2ecf20Sopenharmony_ci * skb to its handler for processing 6808c2ecf20Sopenharmony_ci */ 6818c2ecf20Sopenharmony_ci skb = l3mdev_ip6_out(sk, skb); 6828c2ecf20Sopenharmony_ci if (unlikely(!skb)) 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* Acquire rcu_read_lock() in case we need to use rt->rt6i_idev 6868c2ecf20Sopenharmony_ci * in the error path. Since skb has been freed, the dst could 6878c2ecf20Sopenharmony_ci * have been queued for deletion. 6888c2ecf20Sopenharmony_ci */ 6898c2ecf20Sopenharmony_ci rcu_read_lock(); 6908c2ecf20Sopenharmony_ci IP6_UPD_PO_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len); 6918c2ecf20Sopenharmony_ci err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb, 6928c2ecf20Sopenharmony_ci NULL, rt->dst.dev, dst_output); 6938c2ecf20Sopenharmony_ci if (err > 0) 6948c2ecf20Sopenharmony_ci err = net_xmit_errno(err); 6958c2ecf20Sopenharmony_ci if (err) { 6968c2ecf20Sopenharmony_ci IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); 6978c2ecf20Sopenharmony_ci rcu_read_unlock(); 6988c2ecf20Sopenharmony_ci goto error_check; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci rcu_read_unlock(); 7018c2ecf20Sopenharmony_ciout: 7028c2ecf20Sopenharmony_ci return 0; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cierror: 7058c2ecf20Sopenharmony_ci IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); 7068c2ecf20Sopenharmony_cierror_check: 7078c2ecf20Sopenharmony_ci if (err == -ENOBUFS && !np->recverr) 7088c2ecf20Sopenharmony_ci err = 0; 7098c2ecf20Sopenharmony_ci return err; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistruct raw6_frag_vec { 7138c2ecf20Sopenharmony_ci struct msghdr *msg; 7148c2ecf20Sopenharmony_ci int hlen; 7158c2ecf20Sopenharmony_ci char c[4]; 7168c2ecf20Sopenharmony_ci}; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic int rawv6_probe_proto_opt(struct raw6_frag_vec *rfv, struct flowi6 *fl6) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci int err = 0; 7218c2ecf20Sopenharmony_ci switch (fl6->flowi6_proto) { 7228c2ecf20Sopenharmony_ci case IPPROTO_ICMPV6: 7238c2ecf20Sopenharmony_ci rfv->hlen = 2; 7248c2ecf20Sopenharmony_ci err = memcpy_from_msg(rfv->c, rfv->msg, rfv->hlen); 7258c2ecf20Sopenharmony_ci if (!err) { 7268c2ecf20Sopenharmony_ci fl6->fl6_icmp_type = rfv->c[0]; 7278c2ecf20Sopenharmony_ci fl6->fl6_icmp_code = rfv->c[1]; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci break; 7308c2ecf20Sopenharmony_ci case IPPROTO_MH: 7318c2ecf20Sopenharmony_ci rfv->hlen = 4; 7328c2ecf20Sopenharmony_ci err = memcpy_from_msg(rfv->c, rfv->msg, rfv->hlen); 7338c2ecf20Sopenharmony_ci if (!err) 7348c2ecf20Sopenharmony_ci fl6->fl6_mh_type = rfv->c[2]; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci return err; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic int raw6_getfrag(void *from, char *to, int offset, int len, int odd, 7408c2ecf20Sopenharmony_ci struct sk_buff *skb) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci struct raw6_frag_vec *rfv = from; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (offset < rfv->hlen) { 7458c2ecf20Sopenharmony_ci int copy = min(rfv->hlen - offset, len); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 7488c2ecf20Sopenharmony_ci memcpy(to, rfv->c + offset, copy); 7498c2ecf20Sopenharmony_ci else 7508c2ecf20Sopenharmony_ci skb->csum = csum_block_add( 7518c2ecf20Sopenharmony_ci skb->csum, 7528c2ecf20Sopenharmony_ci csum_partial_copy_nocheck(rfv->c + offset, 7538c2ecf20Sopenharmony_ci to, copy), 7548c2ecf20Sopenharmony_ci odd); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci odd = 0; 7578c2ecf20Sopenharmony_ci offset += copy; 7588c2ecf20Sopenharmony_ci to += copy; 7598c2ecf20Sopenharmony_ci len -= copy; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci if (!len) 7628c2ecf20Sopenharmony_ci return 0; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci offset -= rfv->hlen; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci return ip_generic_getfrag(rfv->msg, to, offset, len, odd, skb); 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct ipv6_txoptions *opt_to_free = NULL; 7738c2ecf20Sopenharmony_ci struct ipv6_txoptions opt_space; 7748c2ecf20Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); 7758c2ecf20Sopenharmony_ci struct in6_addr *daddr, *final_p, final; 7768c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 7778c2ecf20Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 7788c2ecf20Sopenharmony_ci struct raw6_sock *rp = raw6_sk(sk); 7798c2ecf20Sopenharmony_ci struct ipv6_txoptions *opt = NULL; 7808c2ecf20Sopenharmony_ci struct ip6_flowlabel *flowlabel = NULL; 7818c2ecf20Sopenharmony_ci struct dst_entry *dst = NULL; 7828c2ecf20Sopenharmony_ci struct raw6_frag_vec rfv; 7838c2ecf20Sopenharmony_ci struct flowi6 fl6; 7848c2ecf20Sopenharmony_ci struct ipcm6_cookie ipc6; 7858c2ecf20Sopenharmony_ci int addr_len = msg->msg_namelen; 7868c2ecf20Sopenharmony_ci int hdrincl; 7878c2ecf20Sopenharmony_ci u16 proto; 7888c2ecf20Sopenharmony_ci int err; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* Rough check on arithmetic overflow, 7918c2ecf20Sopenharmony_ci better check is made in ip6_append_data(). 7928c2ecf20Sopenharmony_ci */ 7938c2ecf20Sopenharmony_ci if (len > INT_MAX) 7948c2ecf20Sopenharmony_ci return -EMSGSIZE; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* Mirror BSD error message compatibility */ 7978c2ecf20Sopenharmony_ci if (msg->msg_flags & MSG_OOB) 7988c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* hdrincl should be READ_ONCE(inet->hdrincl) 8018c2ecf20Sopenharmony_ci * but READ_ONCE() doesn't work with bit fields. 8028c2ecf20Sopenharmony_ci * Doing this indirectly yields the same result. 8038c2ecf20Sopenharmony_ci */ 8048c2ecf20Sopenharmony_ci hdrincl = inet->hdrincl; 8058c2ecf20Sopenharmony_ci hdrincl = READ_ONCE(hdrincl); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci /* 8088c2ecf20Sopenharmony_ci * Get and verify the address. 8098c2ecf20Sopenharmony_ci */ 8108c2ecf20Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci fl6.flowi6_mark = sk->sk_mark; 8138c2ecf20Sopenharmony_ci fl6.flowi6_uid = sk->sk_uid; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci ipcm6_init(&ipc6); 8168c2ecf20Sopenharmony_ci ipc6.sockc.tsflags = sk->sk_tsflags; 8178c2ecf20Sopenharmony_ci ipc6.sockc.mark = sk->sk_mark; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if (sin6) { 8208c2ecf20Sopenharmony_ci if (addr_len < SIN6_LEN_RFC2133) 8218c2ecf20Sopenharmony_ci return -EINVAL; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (sin6->sin6_family && sin6->sin6_family != AF_INET6) 8248c2ecf20Sopenharmony_ci return -EAFNOSUPPORT; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* port is the proto value [0..255] carried in nexthdr */ 8278c2ecf20Sopenharmony_ci proto = ntohs(sin6->sin6_port); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (!proto) 8308c2ecf20Sopenharmony_ci proto = inet->inet_num; 8318c2ecf20Sopenharmony_ci else if (proto != inet->inet_num && 8328c2ecf20Sopenharmony_ci inet->inet_num != IPPROTO_RAW) 8338c2ecf20Sopenharmony_ci return -EINVAL; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (proto > 255) 8368c2ecf20Sopenharmony_ci return -EINVAL; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci daddr = &sin6->sin6_addr; 8398c2ecf20Sopenharmony_ci if (np->sndflow) { 8408c2ecf20Sopenharmony_ci fl6.flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; 8418c2ecf20Sopenharmony_ci if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) { 8428c2ecf20Sopenharmony_ci flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); 8438c2ecf20Sopenharmony_ci if (IS_ERR(flowlabel)) 8448c2ecf20Sopenharmony_ci return -EINVAL; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* 8498c2ecf20Sopenharmony_ci * Otherwise it will be difficult to maintain 8508c2ecf20Sopenharmony_ci * sk->sk_dst_cache. 8518c2ecf20Sopenharmony_ci */ 8528c2ecf20Sopenharmony_ci if (sk->sk_state == TCP_ESTABLISHED && 8538c2ecf20Sopenharmony_ci ipv6_addr_equal(daddr, &sk->sk_v6_daddr)) 8548c2ecf20Sopenharmony_ci daddr = &sk->sk_v6_daddr; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci if (addr_len >= sizeof(struct sockaddr_in6) && 8578c2ecf20Sopenharmony_ci sin6->sin6_scope_id && 8588c2ecf20Sopenharmony_ci __ipv6_addr_needs_scope_id(__ipv6_addr_type(daddr))) 8598c2ecf20Sopenharmony_ci fl6.flowi6_oif = sin6->sin6_scope_id; 8608c2ecf20Sopenharmony_ci } else { 8618c2ecf20Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) 8628c2ecf20Sopenharmony_ci return -EDESTADDRREQ; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci proto = inet->inet_num; 8658c2ecf20Sopenharmony_ci daddr = &sk->sk_v6_daddr; 8668c2ecf20Sopenharmony_ci fl6.flowlabel = np->flow_label; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (fl6.flowi6_oif == 0) 8708c2ecf20Sopenharmony_ci fl6.flowi6_oif = sk->sk_bound_dev_if; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (msg->msg_controllen) { 8738c2ecf20Sopenharmony_ci opt = &opt_space; 8748c2ecf20Sopenharmony_ci memset(opt, 0, sizeof(struct ipv6_txoptions)); 8758c2ecf20Sopenharmony_ci opt->tot_len = sizeof(struct ipv6_txoptions); 8768c2ecf20Sopenharmony_ci ipc6.opt = opt; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, &ipc6); 8798c2ecf20Sopenharmony_ci if (err < 0) { 8808c2ecf20Sopenharmony_ci fl6_sock_release(flowlabel); 8818c2ecf20Sopenharmony_ci return err; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci if ((fl6.flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) { 8848c2ecf20Sopenharmony_ci flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); 8858c2ecf20Sopenharmony_ci if (IS_ERR(flowlabel)) 8868c2ecf20Sopenharmony_ci return -EINVAL; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci if (!(opt->opt_nflen|opt->opt_flen)) 8898c2ecf20Sopenharmony_ci opt = NULL; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci if (!opt) { 8928c2ecf20Sopenharmony_ci opt = txopt_get(np); 8938c2ecf20Sopenharmony_ci opt_to_free = opt; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci if (flowlabel) 8968c2ecf20Sopenharmony_ci opt = fl6_merge_options(&opt_space, flowlabel, opt); 8978c2ecf20Sopenharmony_ci opt = ipv6_fixup_options(&opt_space, opt); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci fl6.flowi6_proto = proto; 9008c2ecf20Sopenharmony_ci fl6.flowi6_mark = ipc6.sockc.mark; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (!hdrincl) { 9038c2ecf20Sopenharmony_ci rfv.msg = msg; 9048c2ecf20Sopenharmony_ci rfv.hlen = 0; 9058c2ecf20Sopenharmony_ci err = rawv6_probe_proto_opt(&rfv, &fl6); 9068c2ecf20Sopenharmony_ci if (err) 9078c2ecf20Sopenharmony_ci goto out; 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (!ipv6_addr_any(daddr)) 9118c2ecf20Sopenharmony_ci fl6.daddr = *daddr; 9128c2ecf20Sopenharmony_ci else 9138c2ecf20Sopenharmony_ci fl6.daddr.s6_addr[15] = 0x1; /* :: means loopback (BSD'ism) */ 9148c2ecf20Sopenharmony_ci if (ipv6_addr_any(&fl6.saddr) && !ipv6_addr_any(&np->saddr)) 9158c2ecf20Sopenharmony_ci fl6.saddr = np->saddr; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci final_p = fl6_update_dst(&fl6, opt, &final); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) 9208c2ecf20Sopenharmony_ci fl6.flowi6_oif = np->mcast_oif; 9218c2ecf20Sopenharmony_ci else if (!fl6.flowi6_oif) 9228c2ecf20Sopenharmony_ci fl6.flowi6_oif = np->ucast_oif; 9238c2ecf20Sopenharmony_ci security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6)); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci if (hdrincl) 9268c2ecf20Sopenharmony_ci fl6.flowi6_flags |= FLOWI_FLAG_KNOWN_NH; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (ipc6.tclass < 0) 9298c2ecf20Sopenharmony_ci ipc6.tclass = np->tclass; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl6, final_p); 9348c2ecf20Sopenharmony_ci if (IS_ERR(dst)) { 9358c2ecf20Sopenharmony_ci err = PTR_ERR(dst); 9368c2ecf20Sopenharmony_ci goto out; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci if (ipc6.hlimit < 0) 9398c2ecf20Sopenharmony_ci ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci if (ipc6.dontfrag < 0) 9428c2ecf20Sopenharmony_ci ipc6.dontfrag = np->dontfrag; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (msg->msg_flags&MSG_CONFIRM) 9458c2ecf20Sopenharmony_ci goto do_confirm; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ciback_from_confirm: 9488c2ecf20Sopenharmony_ci if (hdrincl) 9498c2ecf20Sopenharmony_ci err = rawv6_send_hdrinc(sk, msg, len, &fl6, &dst, 9508c2ecf20Sopenharmony_ci msg->msg_flags, &ipc6.sockc); 9518c2ecf20Sopenharmony_ci else { 9528c2ecf20Sopenharmony_ci ipc6.opt = opt; 9538c2ecf20Sopenharmony_ci lock_sock(sk); 9548c2ecf20Sopenharmony_ci err = ip6_append_data(sk, raw6_getfrag, &rfv, 9558c2ecf20Sopenharmony_ci len, 0, &ipc6, &fl6, (struct rt6_info *)dst, 9568c2ecf20Sopenharmony_ci msg->msg_flags); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (err) 9598c2ecf20Sopenharmony_ci ip6_flush_pending_frames(sk); 9608c2ecf20Sopenharmony_ci else if (!(msg->msg_flags & MSG_MORE)) 9618c2ecf20Sopenharmony_ci err = rawv6_push_pending_frames(sk, &fl6, rp); 9628c2ecf20Sopenharmony_ci release_sock(sk); 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_cidone: 9658c2ecf20Sopenharmony_ci dst_release(dst); 9668c2ecf20Sopenharmony_ciout: 9678c2ecf20Sopenharmony_ci fl6_sock_release(flowlabel); 9688c2ecf20Sopenharmony_ci txopt_put(opt_to_free); 9698c2ecf20Sopenharmony_ci return err < 0 ? err : len; 9708c2ecf20Sopenharmony_cido_confirm: 9718c2ecf20Sopenharmony_ci if (msg->msg_flags & MSG_PROBE) 9728c2ecf20Sopenharmony_ci dst_confirm_neigh(dst, &fl6.daddr); 9738c2ecf20Sopenharmony_ci if (!(msg->msg_flags & MSG_PROBE) || len) 9748c2ecf20Sopenharmony_ci goto back_from_confirm; 9758c2ecf20Sopenharmony_ci err = 0; 9768c2ecf20Sopenharmony_ci goto done; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic int rawv6_seticmpfilter(struct sock *sk, int level, int optname, 9808c2ecf20Sopenharmony_ci sockptr_t optval, int optlen) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci switch (optname) { 9838c2ecf20Sopenharmony_ci case ICMPV6_FILTER: 9848c2ecf20Sopenharmony_ci if (optlen > sizeof(struct icmp6_filter)) 9858c2ecf20Sopenharmony_ci optlen = sizeof(struct icmp6_filter); 9868c2ecf20Sopenharmony_ci if (copy_from_sockptr(&raw6_sk(sk)->filter, optval, optlen)) 9878c2ecf20Sopenharmony_ci return -EFAULT; 9888c2ecf20Sopenharmony_ci return 0; 9898c2ecf20Sopenharmony_ci default: 9908c2ecf20Sopenharmony_ci return -ENOPROTOOPT; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci return 0; 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int rawv6_geticmpfilter(struct sock *sk, int level, int optname, 9978c2ecf20Sopenharmony_ci char __user *optval, int __user *optlen) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci int len; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci switch (optname) { 10028c2ecf20Sopenharmony_ci case ICMPV6_FILTER: 10038c2ecf20Sopenharmony_ci if (get_user(len, optlen)) 10048c2ecf20Sopenharmony_ci return -EFAULT; 10058c2ecf20Sopenharmony_ci if (len < 0) 10068c2ecf20Sopenharmony_ci return -EINVAL; 10078c2ecf20Sopenharmony_ci if (len > sizeof(struct icmp6_filter)) 10088c2ecf20Sopenharmony_ci len = sizeof(struct icmp6_filter); 10098c2ecf20Sopenharmony_ci if (put_user(len, optlen)) 10108c2ecf20Sopenharmony_ci return -EFAULT; 10118c2ecf20Sopenharmony_ci if (copy_to_user(optval, &raw6_sk(sk)->filter, len)) 10128c2ecf20Sopenharmony_ci return -EFAULT; 10138c2ecf20Sopenharmony_ci return 0; 10148c2ecf20Sopenharmony_ci default: 10158c2ecf20Sopenharmony_ci return -ENOPROTOOPT; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci return 0; 10198c2ecf20Sopenharmony_ci} 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_cistatic int do_rawv6_setsockopt(struct sock *sk, int level, int optname, 10238c2ecf20Sopenharmony_ci sockptr_t optval, unsigned int optlen) 10248c2ecf20Sopenharmony_ci{ 10258c2ecf20Sopenharmony_ci struct raw6_sock *rp = raw6_sk(sk); 10268c2ecf20Sopenharmony_ci int val; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (optlen < sizeof(val)) 10298c2ecf20Sopenharmony_ci return -EINVAL; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if (copy_from_sockptr(&val, optval, sizeof(val))) 10328c2ecf20Sopenharmony_ci return -EFAULT; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci switch (optname) { 10358c2ecf20Sopenharmony_ci case IPV6_HDRINCL: 10368c2ecf20Sopenharmony_ci if (sk->sk_type != SOCK_RAW) 10378c2ecf20Sopenharmony_ci return -EINVAL; 10388c2ecf20Sopenharmony_ci inet_sk(sk)->hdrincl = !!val; 10398c2ecf20Sopenharmony_ci return 0; 10408c2ecf20Sopenharmony_ci case IPV6_CHECKSUM: 10418c2ecf20Sopenharmony_ci if (inet_sk(sk)->inet_num == IPPROTO_ICMPV6 && 10428c2ecf20Sopenharmony_ci level == IPPROTO_IPV6) { 10438c2ecf20Sopenharmony_ci /* 10448c2ecf20Sopenharmony_ci * RFC3542 tells that IPV6_CHECKSUM socket 10458c2ecf20Sopenharmony_ci * option in the IPPROTO_IPV6 level is not 10468c2ecf20Sopenharmony_ci * allowed on ICMPv6 sockets. 10478c2ecf20Sopenharmony_ci * If you want to set it, use IPPROTO_RAW 10488c2ecf20Sopenharmony_ci * level IPV6_CHECKSUM socket option 10498c2ecf20Sopenharmony_ci * (Linux extension). 10508c2ecf20Sopenharmony_ci */ 10518c2ecf20Sopenharmony_ci return -EINVAL; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci /* You may get strange result with a positive odd offset; 10558c2ecf20Sopenharmony_ci RFC2292bis agrees with me. */ 10568c2ecf20Sopenharmony_ci if (val > 0 && (val&1)) 10578c2ecf20Sopenharmony_ci return -EINVAL; 10588c2ecf20Sopenharmony_ci if (val < 0) { 10598c2ecf20Sopenharmony_ci rp->checksum = 0; 10608c2ecf20Sopenharmony_ci } else { 10618c2ecf20Sopenharmony_ci rp->checksum = 1; 10628c2ecf20Sopenharmony_ci rp->offset = val; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci return 0; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci default: 10688c2ecf20Sopenharmony_ci return -ENOPROTOOPT; 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic int rawv6_setsockopt(struct sock *sk, int level, int optname, 10738c2ecf20Sopenharmony_ci sockptr_t optval, unsigned int optlen) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci switch (level) { 10768c2ecf20Sopenharmony_ci case SOL_RAW: 10778c2ecf20Sopenharmony_ci break; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci case SOL_ICMPV6: 10808c2ecf20Sopenharmony_ci if (inet_sk(sk)->inet_num != IPPROTO_ICMPV6) 10818c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 10828c2ecf20Sopenharmony_ci return rawv6_seticmpfilter(sk, level, optname, optval, optlen); 10838c2ecf20Sopenharmony_ci case SOL_IPV6: 10848c2ecf20Sopenharmony_ci if (optname == IPV6_CHECKSUM || 10858c2ecf20Sopenharmony_ci optname == IPV6_HDRINCL) 10868c2ecf20Sopenharmony_ci break; 10878c2ecf20Sopenharmony_ci fallthrough; 10888c2ecf20Sopenharmony_ci default: 10898c2ecf20Sopenharmony_ci return ipv6_setsockopt(sk, level, optname, optval, optlen); 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci return do_rawv6_setsockopt(sk, level, optname, optval, optlen); 10938c2ecf20Sopenharmony_ci} 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_cistatic int do_rawv6_getsockopt(struct sock *sk, int level, int optname, 10968c2ecf20Sopenharmony_ci char __user *optval, int __user *optlen) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci struct raw6_sock *rp = raw6_sk(sk); 10998c2ecf20Sopenharmony_ci int val, len; 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci if (get_user(len, optlen)) 11028c2ecf20Sopenharmony_ci return -EFAULT; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci switch (optname) { 11058c2ecf20Sopenharmony_ci case IPV6_HDRINCL: 11068c2ecf20Sopenharmony_ci val = inet_sk(sk)->hdrincl; 11078c2ecf20Sopenharmony_ci break; 11088c2ecf20Sopenharmony_ci case IPV6_CHECKSUM: 11098c2ecf20Sopenharmony_ci /* 11108c2ecf20Sopenharmony_ci * We allow getsockopt() for IPPROTO_IPV6-level 11118c2ecf20Sopenharmony_ci * IPV6_CHECKSUM socket option on ICMPv6 sockets 11128c2ecf20Sopenharmony_ci * since RFC3542 is silent about it. 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_ci if (rp->checksum == 0) 11158c2ecf20Sopenharmony_ci val = -1; 11168c2ecf20Sopenharmony_ci else 11178c2ecf20Sopenharmony_ci val = rp->offset; 11188c2ecf20Sopenharmony_ci break; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci default: 11218c2ecf20Sopenharmony_ci return -ENOPROTOOPT; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci len = min_t(unsigned int, sizeof(int), len); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if (put_user(len, optlen)) 11278c2ecf20Sopenharmony_ci return -EFAULT; 11288c2ecf20Sopenharmony_ci if (copy_to_user(optval, &val, len)) 11298c2ecf20Sopenharmony_ci return -EFAULT; 11308c2ecf20Sopenharmony_ci return 0; 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cistatic int rawv6_getsockopt(struct sock *sk, int level, int optname, 11348c2ecf20Sopenharmony_ci char __user *optval, int __user *optlen) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci switch (level) { 11378c2ecf20Sopenharmony_ci case SOL_RAW: 11388c2ecf20Sopenharmony_ci break; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci case SOL_ICMPV6: 11418c2ecf20Sopenharmony_ci if (inet_sk(sk)->inet_num != IPPROTO_ICMPV6) 11428c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 11438c2ecf20Sopenharmony_ci return rawv6_geticmpfilter(sk, level, optname, optval, optlen); 11448c2ecf20Sopenharmony_ci case SOL_IPV6: 11458c2ecf20Sopenharmony_ci if (optname == IPV6_CHECKSUM || 11468c2ecf20Sopenharmony_ci optname == IPV6_HDRINCL) 11478c2ecf20Sopenharmony_ci break; 11488c2ecf20Sopenharmony_ci fallthrough; 11498c2ecf20Sopenharmony_ci default: 11508c2ecf20Sopenharmony_ci return ipv6_getsockopt(sk, level, optname, optval, optlen); 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci return do_rawv6_getsockopt(sk, level, optname, optval, optlen); 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic int rawv6_ioctl(struct sock *sk, int cmd, unsigned long arg) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci switch (cmd) { 11598c2ecf20Sopenharmony_ci case SIOCOUTQ: { 11608c2ecf20Sopenharmony_ci int amount = sk_wmem_alloc_get(sk); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci return put_user(amount, (int __user *)arg); 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci case SIOCINQ: { 11658c2ecf20Sopenharmony_ci struct sk_buff *skb; 11668c2ecf20Sopenharmony_ci int amount = 0; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci spin_lock_bh(&sk->sk_receive_queue.lock); 11698c2ecf20Sopenharmony_ci skb = skb_peek(&sk->sk_receive_queue); 11708c2ecf20Sopenharmony_ci if (skb) 11718c2ecf20Sopenharmony_ci amount = skb->len; 11728c2ecf20Sopenharmony_ci spin_unlock_bh(&sk->sk_receive_queue.lock); 11738c2ecf20Sopenharmony_ci return put_user(amount, (int __user *)arg); 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci default: 11778c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 11788c2ecf20Sopenharmony_ci return ip6mr_ioctl(sk, cmd, (void __user *)arg); 11798c2ecf20Sopenharmony_ci#else 11808c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 11818c2ecf20Sopenharmony_ci#endif 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 11868c2ecf20Sopenharmony_cistatic int compat_rawv6_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci switch (cmd) { 11898c2ecf20Sopenharmony_ci case SIOCOUTQ: 11908c2ecf20Sopenharmony_ci case SIOCINQ: 11918c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 11928c2ecf20Sopenharmony_ci default: 11938c2ecf20Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 11948c2ecf20Sopenharmony_ci return ip6mr_compat_ioctl(sk, cmd, compat_ptr(arg)); 11958c2ecf20Sopenharmony_ci#else 11968c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 11978c2ecf20Sopenharmony_ci#endif 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci#endif 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_cistatic void rawv6_close(struct sock *sk, long timeout) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci if (inet_sk(sk)->inet_num == IPPROTO_RAW) 12058c2ecf20Sopenharmony_ci ip6_ra_control(sk, -1); 12068c2ecf20Sopenharmony_ci ip6mr_sk_done(sk); 12078c2ecf20Sopenharmony_ci sk_common_release(sk); 12088c2ecf20Sopenharmony_ci} 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_cistatic void raw6_destroy(struct sock *sk) 12118c2ecf20Sopenharmony_ci{ 12128c2ecf20Sopenharmony_ci lock_sock(sk); 12138c2ecf20Sopenharmony_ci ip6_flush_pending_frames(sk); 12148c2ecf20Sopenharmony_ci release_sock(sk); 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic int rawv6_init_sk(struct sock *sk) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci struct raw6_sock *rp = raw6_sk(sk); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci switch (inet_sk(sk)->inet_num) { 12228c2ecf20Sopenharmony_ci case IPPROTO_ICMPV6: 12238c2ecf20Sopenharmony_ci rp->checksum = 1; 12248c2ecf20Sopenharmony_ci rp->offset = 2; 12258c2ecf20Sopenharmony_ci break; 12268c2ecf20Sopenharmony_ci case IPPROTO_MH: 12278c2ecf20Sopenharmony_ci rp->checksum = 1; 12288c2ecf20Sopenharmony_ci rp->offset = 4; 12298c2ecf20Sopenharmony_ci break; 12308c2ecf20Sopenharmony_ci default: 12318c2ecf20Sopenharmony_ci break; 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci return 0; 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistruct proto rawv6_prot = { 12378c2ecf20Sopenharmony_ci .name = "RAWv6", 12388c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 12398c2ecf20Sopenharmony_ci .close = rawv6_close, 12408c2ecf20Sopenharmony_ci .destroy = raw6_destroy, 12418c2ecf20Sopenharmony_ci .connect = ip6_datagram_connect_v6_only, 12428c2ecf20Sopenharmony_ci .disconnect = __udp_disconnect, 12438c2ecf20Sopenharmony_ci .ioctl = rawv6_ioctl, 12448c2ecf20Sopenharmony_ci .init = rawv6_init_sk, 12458c2ecf20Sopenharmony_ci .setsockopt = rawv6_setsockopt, 12468c2ecf20Sopenharmony_ci .getsockopt = rawv6_getsockopt, 12478c2ecf20Sopenharmony_ci .sendmsg = rawv6_sendmsg, 12488c2ecf20Sopenharmony_ci .recvmsg = rawv6_recvmsg, 12498c2ecf20Sopenharmony_ci .bind = rawv6_bind, 12508c2ecf20Sopenharmony_ci .backlog_rcv = rawv6_rcv_skb, 12518c2ecf20Sopenharmony_ci .hash = raw_hash_sk, 12528c2ecf20Sopenharmony_ci .unhash = raw_unhash_sk, 12538c2ecf20Sopenharmony_ci .obj_size = sizeof(struct raw6_sock), 12548c2ecf20Sopenharmony_ci .useroffset = offsetof(struct raw6_sock, filter), 12558c2ecf20Sopenharmony_ci .usersize = sizeof_field(struct raw6_sock, filter), 12568c2ecf20Sopenharmony_ci .h.raw_hash = &raw_v6_hashinfo, 12578c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 12588c2ecf20Sopenharmony_ci .compat_ioctl = compat_rawv6_ioctl, 12598c2ecf20Sopenharmony_ci#endif 12608c2ecf20Sopenharmony_ci .diag_destroy = raw_abort, 12618c2ecf20Sopenharmony_ci}; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 12648c2ecf20Sopenharmony_cistatic int raw6_seq_show(struct seq_file *seq, void *v) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) { 12678c2ecf20Sopenharmony_ci seq_puts(seq, IPV6_SEQ_DGRAM_HEADER); 12688c2ecf20Sopenharmony_ci } else { 12698c2ecf20Sopenharmony_ci struct sock *sp = v; 12708c2ecf20Sopenharmony_ci __u16 srcp = inet_sk(sp)->inet_num; 12718c2ecf20Sopenharmony_ci ip6_dgram_sock_seq_show(seq, v, srcp, 0, 12728c2ecf20Sopenharmony_ci raw_seq_private(seq)->bucket); 12738c2ecf20Sopenharmony_ci } 12748c2ecf20Sopenharmony_ci return 0; 12758c2ecf20Sopenharmony_ci} 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cistatic const struct seq_operations raw6_seq_ops = { 12788c2ecf20Sopenharmony_ci .start = raw_seq_start, 12798c2ecf20Sopenharmony_ci .next = raw_seq_next, 12808c2ecf20Sopenharmony_ci .stop = raw_seq_stop, 12818c2ecf20Sopenharmony_ci .show = raw6_seq_show, 12828c2ecf20Sopenharmony_ci}; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_cistatic int __net_init raw6_init_net(struct net *net) 12858c2ecf20Sopenharmony_ci{ 12868c2ecf20Sopenharmony_ci if (!proc_create_net_data("raw6", 0444, net->proc_net, &raw6_seq_ops, 12878c2ecf20Sopenharmony_ci sizeof(struct raw_iter_state), &raw_v6_hashinfo)) 12888c2ecf20Sopenharmony_ci return -ENOMEM; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci return 0; 12918c2ecf20Sopenharmony_ci} 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_cistatic void __net_exit raw6_exit_net(struct net *net) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci remove_proc_entry("raw6", net->proc_net); 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cistatic struct pernet_operations raw6_net_ops = { 12998c2ecf20Sopenharmony_ci .init = raw6_init_net, 13008c2ecf20Sopenharmony_ci .exit = raw6_exit_net, 13018c2ecf20Sopenharmony_ci}; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ciint __init raw6_proc_init(void) 13048c2ecf20Sopenharmony_ci{ 13058c2ecf20Sopenharmony_ci return register_pernet_subsys(&raw6_net_ops); 13068c2ecf20Sopenharmony_ci} 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_civoid raw6_proc_exit(void) 13098c2ecf20Sopenharmony_ci{ 13108c2ecf20Sopenharmony_ci unregister_pernet_subsys(&raw6_net_ops); 13118c2ecf20Sopenharmony_ci} 13128c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci/* Same as inet6_dgram_ops, sans udp_poll. */ 13158c2ecf20Sopenharmony_ciconst struct proto_ops inet6_sockraw_ops = { 13168c2ecf20Sopenharmony_ci .family = PF_INET6, 13178c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 13188c2ecf20Sopenharmony_ci .release = inet6_release, 13198c2ecf20Sopenharmony_ci .bind = inet6_bind, 13208c2ecf20Sopenharmony_ci .connect = inet_dgram_connect, /* ok */ 13218c2ecf20Sopenharmony_ci .socketpair = sock_no_socketpair, /* a do nothing */ 13228c2ecf20Sopenharmony_ci .accept = sock_no_accept, /* a do nothing */ 13238c2ecf20Sopenharmony_ci .getname = inet6_getname, 13248c2ecf20Sopenharmony_ci .poll = datagram_poll, /* ok */ 13258c2ecf20Sopenharmony_ci .ioctl = inet6_ioctl, /* must change */ 13268c2ecf20Sopenharmony_ci .gettstamp = sock_gettstamp, 13278c2ecf20Sopenharmony_ci .listen = sock_no_listen, /* ok */ 13288c2ecf20Sopenharmony_ci .shutdown = inet_shutdown, /* ok */ 13298c2ecf20Sopenharmony_ci .setsockopt = sock_common_setsockopt, /* ok */ 13308c2ecf20Sopenharmony_ci .getsockopt = sock_common_getsockopt, /* ok */ 13318c2ecf20Sopenharmony_ci .sendmsg = inet_sendmsg, /* ok */ 13328c2ecf20Sopenharmony_ci .recvmsg = sock_common_recvmsg, /* ok */ 13338c2ecf20Sopenharmony_ci .mmap = sock_no_mmap, 13348c2ecf20Sopenharmony_ci .sendpage = sock_no_sendpage, 13358c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 13368c2ecf20Sopenharmony_ci .compat_ioctl = inet6_compat_ioctl, 13378c2ecf20Sopenharmony_ci#endif 13388c2ecf20Sopenharmony_ci}; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic struct inet_protosw rawv6_protosw = { 13418c2ecf20Sopenharmony_ci .type = SOCK_RAW, 13428c2ecf20Sopenharmony_ci .protocol = IPPROTO_IP, /* wild card */ 13438c2ecf20Sopenharmony_ci .prot = &rawv6_prot, 13448c2ecf20Sopenharmony_ci .ops = &inet6_sockraw_ops, 13458c2ecf20Sopenharmony_ci .flags = INET_PROTOSW_REUSE, 13468c2ecf20Sopenharmony_ci}; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ciint __init rawv6_init(void) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci return inet6_register_protosw(&rawv6_protosw); 13518c2ecf20Sopenharmony_ci} 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_civoid rawv6_exit(void) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci inet6_unregister_protosw(&rawv6_protosw); 13568c2ecf20Sopenharmony_ci} 1357