162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * RAW sockets for IPv6 462306a36Sopenharmony_ci * Linux INET6 implementation 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Pedro Roque <roque@di.fc.ul.pt> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Adapted from linux/net/ipv4/raw.c 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Fixes: 1262306a36Sopenharmony_ci * Hideaki YOSHIFUJI : sin6_scope_id support 1362306a36Sopenharmony_ci * YOSHIFUJI,H.@USAGI : raw checksum (RFC2292(bis) compliance) 1462306a36Sopenharmony_ci * Kazunori MIYAZAWA @USAGI: change process style to use ip6_append_data 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/errno.h> 1862306a36Sopenharmony_ci#include <linux/types.h> 1962306a36Sopenharmony_ci#include <linux/socket.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/sockios.h> 2262306a36Sopenharmony_ci#include <linux/net.h> 2362306a36Sopenharmony_ci#include <linux/in6.h> 2462306a36Sopenharmony_ci#include <linux/netdevice.h> 2562306a36Sopenharmony_ci#include <linux/if_arp.h> 2662306a36Sopenharmony_ci#include <linux/icmpv6.h> 2762306a36Sopenharmony_ci#include <linux/netfilter.h> 2862306a36Sopenharmony_ci#include <linux/netfilter_ipv6.h> 2962306a36Sopenharmony_ci#include <linux/skbuff.h> 3062306a36Sopenharmony_ci#include <linux/compat.h> 3162306a36Sopenharmony_ci#include <linux/uaccess.h> 3262306a36Sopenharmony_ci#include <asm/ioctls.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <net/net_namespace.h> 3562306a36Sopenharmony_ci#include <net/ip.h> 3662306a36Sopenharmony_ci#include <net/sock.h> 3762306a36Sopenharmony_ci#include <net/snmp.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <net/ipv6.h> 4062306a36Sopenharmony_ci#include <net/ndisc.h> 4162306a36Sopenharmony_ci#include <net/protocol.h> 4262306a36Sopenharmony_ci#include <net/ip6_route.h> 4362306a36Sopenharmony_ci#include <net/ip6_checksum.h> 4462306a36Sopenharmony_ci#include <net/addrconf.h> 4562306a36Sopenharmony_ci#include <net/transp_v6.h> 4662306a36Sopenharmony_ci#include <net/udp.h> 4762306a36Sopenharmony_ci#include <net/inet_common.h> 4862306a36Sopenharmony_ci#include <net/tcp_states.h> 4962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 5062306a36Sopenharmony_ci#include <net/mip6.h> 5162306a36Sopenharmony_ci#endif 5262306a36Sopenharmony_ci#include <linux/mroute6.h> 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#include <net/raw.h> 5562306a36Sopenharmony_ci#include <net/rawv6.h> 5662306a36Sopenharmony_ci#include <net/xfrm.h> 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#include <linux/proc_fs.h> 5962306a36Sopenharmony_ci#include <linux/seq_file.h> 6062306a36Sopenharmony_ci#include <linux/export.h> 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define ICMPV6_HDRLEN 4 /* ICMPv6 header, RFC 4443 Section 2.1 */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistruct raw_hashinfo raw_v6_hashinfo; 6562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(raw_v6_hashinfo); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cibool raw_v6_match(struct net *net, const struct sock *sk, unsigned short num, 6862306a36Sopenharmony_ci const struct in6_addr *loc_addr, 6962306a36Sopenharmony_ci const struct in6_addr *rmt_addr, int dif, int sdif) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci if (inet_sk(sk)->inet_num != num || 7262306a36Sopenharmony_ci !net_eq(sock_net(sk), net) || 7362306a36Sopenharmony_ci (!ipv6_addr_any(&sk->sk_v6_daddr) && 7462306a36Sopenharmony_ci !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr)) || 7562306a36Sopenharmony_ci !raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if, 7662306a36Sopenharmony_ci dif, sdif)) 7762306a36Sopenharmony_ci return false; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (ipv6_addr_any(&sk->sk_v6_rcv_saddr) || 8062306a36Sopenharmony_ci ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr) || 8162306a36Sopenharmony_ci (ipv6_addr_is_multicast(loc_addr) && 8262306a36Sopenharmony_ci inet6_mc_check(sk, loc_addr, rmt_addr))) 8362306a36Sopenharmony_ci return true; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return false; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(raw_v6_match); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* 9062306a36Sopenharmony_ci * 0 - deliver 9162306a36Sopenharmony_ci * 1 - block 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_cistatic int icmpv6_filter(const struct sock *sk, const struct sk_buff *skb) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct icmp6hdr _hdr; 9662306a36Sopenharmony_ci const struct icmp6hdr *hdr; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* We require only the four bytes of the ICMPv6 header, not any 9962306a36Sopenharmony_ci * additional bytes of message body in "struct icmp6hdr". 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci hdr = skb_header_pointer(skb, skb_transport_offset(skb), 10262306a36Sopenharmony_ci ICMPV6_HDRLEN, &_hdr); 10362306a36Sopenharmony_ci if (hdr) { 10462306a36Sopenharmony_ci const __u32 *data = &raw6_sk(sk)->filter.data[0]; 10562306a36Sopenharmony_ci unsigned int type = hdr->icmp6_type; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return (data[type >> 5] & (1U << (type & 31))) != 0; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci return 1; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 11362306a36Sopenharmony_citypedef int mh_filter_t(struct sock *sock, struct sk_buff *skb); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic mh_filter_t __rcu *mh_filter __read_mostly; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciint rawv6_mh_filter_register(mh_filter_t filter) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci rcu_assign_pointer(mh_filter, filter); 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ciEXPORT_SYMBOL(rawv6_mh_filter_register); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ciint rawv6_mh_filter_unregister(mh_filter_t filter) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci RCU_INIT_POINTER(mh_filter, NULL); 12762306a36Sopenharmony_ci synchronize_rcu(); 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ciEXPORT_SYMBOL(rawv6_mh_filter_unregister); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#endif 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* 13562306a36Sopenharmony_ci * demultiplex raw sockets. 13662306a36Sopenharmony_ci * (should consider queueing the skb in the sock receive_queue 13762306a36Sopenharmony_ci * without calling rawv6.c) 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * Caller owns SKB so we must make clones. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic bool ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 14462306a36Sopenharmony_ci const struct in6_addr *saddr; 14562306a36Sopenharmony_ci const struct in6_addr *daddr; 14662306a36Sopenharmony_ci struct hlist_head *hlist; 14762306a36Sopenharmony_ci struct sock *sk; 14862306a36Sopenharmony_ci bool delivered = false; 14962306a36Sopenharmony_ci __u8 hash; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci saddr = &ipv6_hdr(skb)->saddr; 15262306a36Sopenharmony_ci daddr = saddr + 1; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci hash = raw_hashfunc(net, nexthdr); 15562306a36Sopenharmony_ci hlist = &raw_v6_hashinfo.ht[hash]; 15662306a36Sopenharmony_ci rcu_read_lock(); 15762306a36Sopenharmony_ci sk_for_each_rcu(sk, hlist) { 15862306a36Sopenharmony_ci int filtered; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (!raw_v6_match(net, sk, nexthdr, daddr, saddr, 16162306a36Sopenharmony_ci inet6_iif(skb), inet6_sdif(skb))) 16262306a36Sopenharmony_ci continue; 16362306a36Sopenharmony_ci delivered = true; 16462306a36Sopenharmony_ci switch (nexthdr) { 16562306a36Sopenharmony_ci case IPPROTO_ICMPV6: 16662306a36Sopenharmony_ci filtered = icmpv6_filter(sk, skb); 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6_MIP6) 17062306a36Sopenharmony_ci case IPPROTO_MH: 17162306a36Sopenharmony_ci { 17262306a36Sopenharmony_ci /* XXX: To validate MH only once for each packet, 17362306a36Sopenharmony_ci * this is placed here. It should be after checking 17462306a36Sopenharmony_ci * xfrm policy, however it doesn't. The checking xfrm 17562306a36Sopenharmony_ci * policy is placed in rawv6_rcv() because it is 17662306a36Sopenharmony_ci * required for each socket. 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci mh_filter_t *filter; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci filter = rcu_dereference(mh_filter); 18162306a36Sopenharmony_ci filtered = filter ? (*filter)(sk, skb) : 0; 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci#endif 18562306a36Sopenharmony_ci default: 18662306a36Sopenharmony_ci filtered = 0; 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (filtered < 0) 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci if (filtered == 0) { 19362306a36Sopenharmony_ci struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Not releasing hash table! */ 19662306a36Sopenharmony_ci if (clone) 19762306a36Sopenharmony_ci rawv6_rcv(sk, clone); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci rcu_read_unlock(); 20162306a36Sopenharmony_ci return delivered; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cibool raw6_local_deliver(struct sk_buff *skb, int nexthdr) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci return ipv6_raw_deliver(skb, nexthdr); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* This cleans up af_inet6 a bit. -DaveM */ 21062306a36Sopenharmony_cistatic int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 21362306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 21462306a36Sopenharmony_ci struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr; 21562306a36Sopenharmony_ci __be32 v4addr = 0; 21662306a36Sopenharmony_ci int addr_type; 21762306a36Sopenharmony_ci int err; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (addr_len < SIN6_LEN_RFC2133) 22062306a36Sopenharmony_ci return -EINVAL; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (addr->sin6_family != AF_INET6) 22362306a36Sopenharmony_ci return -EINVAL; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci addr_type = ipv6_addr_type(&addr->sin6_addr); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Raw sockets are IPv6 only */ 22862306a36Sopenharmony_ci if (addr_type == IPV6_ADDR_MAPPED) 22962306a36Sopenharmony_ci return -EADDRNOTAVAIL; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci lock_sock(sk); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci err = -EINVAL; 23462306a36Sopenharmony_ci if (sk->sk_state != TCP_CLOSE) 23562306a36Sopenharmony_ci goto out; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci rcu_read_lock(); 23862306a36Sopenharmony_ci /* Check if the address belongs to the host. */ 23962306a36Sopenharmony_ci if (addr_type != IPV6_ADDR_ANY) { 24062306a36Sopenharmony_ci struct net_device *dev = NULL; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (__ipv6_addr_needs_scope_id(addr_type)) { 24362306a36Sopenharmony_ci if (addr_len >= sizeof(struct sockaddr_in6) && 24462306a36Sopenharmony_ci addr->sin6_scope_id) { 24562306a36Sopenharmony_ci /* Override any existing binding, if another 24662306a36Sopenharmony_ci * one is supplied by user. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci sk->sk_bound_dev_if = addr->sin6_scope_id; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Binding to link-local address requires an interface */ 25262306a36Sopenharmony_ci if (!sk->sk_bound_dev_if) 25362306a36Sopenharmony_ci goto out_unlock; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (sk->sk_bound_dev_if) { 25762306a36Sopenharmony_ci err = -ENODEV; 25862306a36Sopenharmony_ci dev = dev_get_by_index_rcu(sock_net(sk), 25962306a36Sopenharmony_ci sk->sk_bound_dev_if); 26062306a36Sopenharmony_ci if (!dev) 26162306a36Sopenharmony_ci goto out_unlock; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* ipv4 addr of the socket is invalid. Only the 26562306a36Sopenharmony_ci * unspecified and mapped address have a v4 equivalent. 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci v4addr = LOOPBACK4_IPV6; 26862306a36Sopenharmony_ci if (!(addr_type & IPV6_ADDR_MULTICAST) && 26962306a36Sopenharmony_ci !ipv6_can_nonlocal_bind(sock_net(sk), inet)) { 27062306a36Sopenharmony_ci err = -EADDRNOTAVAIL; 27162306a36Sopenharmony_ci if (!ipv6_chk_addr(sock_net(sk), &addr->sin6_addr, 27262306a36Sopenharmony_ci dev, 0)) { 27362306a36Sopenharmony_ci goto out_unlock; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci inet->inet_rcv_saddr = inet->inet_saddr = v4addr; 27962306a36Sopenharmony_ci sk->sk_v6_rcv_saddr = addr->sin6_addr; 28062306a36Sopenharmony_ci if (!(addr_type & IPV6_ADDR_MULTICAST)) 28162306a36Sopenharmony_ci np->saddr = addr->sin6_addr; 28262306a36Sopenharmony_ci err = 0; 28362306a36Sopenharmony_ciout_unlock: 28462306a36Sopenharmony_ci rcu_read_unlock(); 28562306a36Sopenharmony_ciout: 28662306a36Sopenharmony_ci release_sock(sk); 28762306a36Sopenharmony_ci return err; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic void rawv6_err(struct sock *sk, struct sk_buff *skb, 29162306a36Sopenharmony_ci struct inet6_skb_parm *opt, 29262306a36Sopenharmony_ci u8 type, u8 code, int offset, __be32 info) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 29562306a36Sopenharmony_ci int err; 29662306a36Sopenharmony_ci int harderr; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Report error on raw socket, if: 29962306a36Sopenharmony_ci 1. User requested recverr. 30062306a36Sopenharmony_ci 2. Socket is connected (otherwise the error indication 30162306a36Sopenharmony_ci is useless without recverr and error is hard. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci if (!np->recverr && sk->sk_state != TCP_ESTABLISHED) 30462306a36Sopenharmony_ci return; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci harderr = icmpv6_err_convert(type, code, &err); 30762306a36Sopenharmony_ci if (type == ICMPV6_PKT_TOOBIG) { 30862306a36Sopenharmony_ci ip6_sk_update_pmtu(skb, sk, info); 30962306a36Sopenharmony_ci harderr = (np->pmtudisc == IPV6_PMTUDISC_DO); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci if (type == NDISC_REDIRECT) { 31262306a36Sopenharmony_ci ip6_sk_redirect(skb, sk); 31362306a36Sopenharmony_ci return; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci if (np->recverr) { 31662306a36Sopenharmony_ci u8 *payload = skb->data; 31762306a36Sopenharmony_ci if (!inet_test_bit(HDRINCL, sk)) 31862306a36Sopenharmony_ci payload += offset; 31962306a36Sopenharmony_ci ipv6_icmp_error(sk, skb, err, 0, ntohl(info), payload); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (np->recverr || harderr) { 32362306a36Sopenharmony_ci sk->sk_err = err; 32462306a36Sopenharmony_ci sk_error_report(sk); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_civoid raw6_icmp_error(struct sk_buff *skb, int nexthdr, 32962306a36Sopenharmony_ci u8 type, u8 code, int inner_offset, __be32 info) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct net *net = dev_net(skb->dev); 33262306a36Sopenharmony_ci struct hlist_head *hlist; 33362306a36Sopenharmony_ci struct sock *sk; 33462306a36Sopenharmony_ci int hash; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci hash = raw_hashfunc(net, nexthdr); 33762306a36Sopenharmony_ci hlist = &raw_v6_hashinfo.ht[hash]; 33862306a36Sopenharmony_ci rcu_read_lock(); 33962306a36Sopenharmony_ci sk_for_each_rcu(sk, hlist) { 34062306a36Sopenharmony_ci /* Note: ipv6_hdr(skb) != skb->data */ 34162306a36Sopenharmony_ci const struct ipv6hdr *ip6h = (const struct ipv6hdr *)skb->data; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (!raw_v6_match(net, sk, nexthdr, &ip6h->saddr, &ip6h->daddr, 34462306a36Sopenharmony_ci inet6_iif(skb), inet6_iif(skb))) 34562306a36Sopenharmony_ci continue; 34662306a36Sopenharmony_ci rawv6_err(sk, skb, NULL, type, code, inner_offset, info); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci rcu_read_unlock(); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic inline int rawv6_rcv_skb(struct sock *sk, struct sk_buff *skb) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci enum skb_drop_reason reason; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if ((raw6_sk(sk)->checksum || rcu_access_pointer(sk->sk_filter)) && 35662306a36Sopenharmony_ci skb_checksum_complete(skb)) { 35762306a36Sopenharmony_ci atomic_inc(&sk->sk_drops); 35862306a36Sopenharmony_ci kfree_skb_reason(skb, SKB_DROP_REASON_SKB_CSUM); 35962306a36Sopenharmony_ci return NET_RX_DROP; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Charge it to the socket. */ 36362306a36Sopenharmony_ci skb_dst_drop(skb); 36462306a36Sopenharmony_ci if (sock_queue_rcv_skb_reason(sk, skb, &reason) < 0) { 36562306a36Sopenharmony_ci kfree_skb_reason(skb, reason); 36662306a36Sopenharmony_ci return NET_RX_DROP; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci/* 37362306a36Sopenharmony_ci * This is next to useless... 37462306a36Sopenharmony_ci * if we demultiplex in network layer we don't need the extra call 37562306a36Sopenharmony_ci * just to queue the skb... 37662306a36Sopenharmony_ci * maybe we could have the network decide upon a hint if it 37762306a36Sopenharmony_ci * should call raw_rcv for demultiplexing 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ciint rawv6_rcv(struct sock *sk, struct sk_buff *skb) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 38262306a36Sopenharmony_ci struct raw6_sock *rp = raw6_sk(sk); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) { 38562306a36Sopenharmony_ci atomic_inc(&sk->sk_drops); 38662306a36Sopenharmony_ci kfree_skb_reason(skb, SKB_DROP_REASON_XFRM_POLICY); 38762306a36Sopenharmony_ci return NET_RX_DROP; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci nf_reset_ct(skb); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (!rp->checksum) 39262306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_COMPLETE) { 39562306a36Sopenharmony_ci skb_postpull_rcsum(skb, skb_network_header(skb), 39662306a36Sopenharmony_ci skb_network_header_len(skb)); 39762306a36Sopenharmony_ci if (!csum_ipv6_magic(&ipv6_hdr(skb)->saddr, 39862306a36Sopenharmony_ci &ipv6_hdr(skb)->daddr, 39962306a36Sopenharmony_ci skb->len, inet->inet_num, skb->csum)) 40062306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci if (!skb_csum_unnecessary(skb)) 40362306a36Sopenharmony_ci skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr, 40462306a36Sopenharmony_ci &ipv6_hdr(skb)->daddr, 40562306a36Sopenharmony_ci skb->len, 40662306a36Sopenharmony_ci inet->inet_num, 0)); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (inet_test_bit(HDRINCL, sk)) { 40962306a36Sopenharmony_ci if (skb_checksum_complete(skb)) { 41062306a36Sopenharmony_ci atomic_inc(&sk->sk_drops); 41162306a36Sopenharmony_ci kfree_skb_reason(skb, SKB_DROP_REASON_SKB_CSUM); 41262306a36Sopenharmony_ci return NET_RX_DROP; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci rawv6_rcv_skb(sk, skb); 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/* 42262306a36Sopenharmony_ci * This should be easy, if there is something there 42362306a36Sopenharmony_ci * we return it, otherwise we block. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, 42762306a36Sopenharmony_ci int flags, int *addr_len) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 43062306a36Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); 43162306a36Sopenharmony_ci struct sk_buff *skb; 43262306a36Sopenharmony_ci size_t copied; 43362306a36Sopenharmony_ci int err; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (flags & MSG_OOB) 43662306a36Sopenharmony_ci return -EOPNOTSUPP; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (flags & MSG_ERRQUEUE) 43962306a36Sopenharmony_ci return ipv6_recv_error(sk, msg, len, addr_len); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (np->rxpmtu && np->rxopt.bits.rxpmtu) 44262306a36Sopenharmony_ci return ipv6_recv_rxpmtu(sk, msg, len, addr_len); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci skb = skb_recv_datagram(sk, flags, &err); 44562306a36Sopenharmony_ci if (!skb) 44662306a36Sopenharmony_ci goto out; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci copied = skb->len; 44962306a36Sopenharmony_ci if (copied > len) { 45062306a36Sopenharmony_ci copied = len; 45162306a36Sopenharmony_ci msg->msg_flags |= MSG_TRUNC; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (skb_csum_unnecessary(skb)) { 45562306a36Sopenharmony_ci err = skb_copy_datagram_msg(skb, 0, msg, copied); 45662306a36Sopenharmony_ci } else if (msg->msg_flags&MSG_TRUNC) { 45762306a36Sopenharmony_ci if (__skb_checksum_complete(skb)) 45862306a36Sopenharmony_ci goto csum_copy_err; 45962306a36Sopenharmony_ci err = skb_copy_datagram_msg(skb, 0, msg, copied); 46062306a36Sopenharmony_ci } else { 46162306a36Sopenharmony_ci err = skb_copy_and_csum_datagram_msg(skb, 0, msg); 46262306a36Sopenharmony_ci if (err == -EINVAL) 46362306a36Sopenharmony_ci goto csum_copy_err; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci if (err) 46662306a36Sopenharmony_ci goto out_free; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* Copy the address. */ 46962306a36Sopenharmony_ci if (sin6) { 47062306a36Sopenharmony_ci sin6->sin6_family = AF_INET6; 47162306a36Sopenharmony_ci sin6->sin6_port = 0; 47262306a36Sopenharmony_ci sin6->sin6_addr = ipv6_hdr(skb)->saddr; 47362306a36Sopenharmony_ci sin6->sin6_flowinfo = 0; 47462306a36Sopenharmony_ci sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr, 47562306a36Sopenharmony_ci inet6_iif(skb)); 47662306a36Sopenharmony_ci *addr_len = sizeof(*sin6); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci sock_recv_cmsgs(msg, sk, skb); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (np->rxopt.all) 48262306a36Sopenharmony_ci ip6_datagram_recv_ctl(sk, msg, skb); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci err = copied; 48562306a36Sopenharmony_ci if (flags & MSG_TRUNC) 48662306a36Sopenharmony_ci err = skb->len; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ciout_free: 48962306a36Sopenharmony_ci skb_free_datagram(sk, skb); 49062306a36Sopenharmony_ciout: 49162306a36Sopenharmony_ci return err; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cicsum_copy_err: 49462306a36Sopenharmony_ci skb_kill_datagram(sk, skb, flags); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* Error for blocking case is chosen to masquerade 49762306a36Sopenharmony_ci as some normal condition. 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_ci err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH; 50062306a36Sopenharmony_ci goto out; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, 50462306a36Sopenharmony_ci struct raw6_sock *rp) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct ipv6_txoptions *opt; 50762306a36Sopenharmony_ci struct sk_buff *skb; 50862306a36Sopenharmony_ci int err = 0; 50962306a36Sopenharmony_ci int offset; 51062306a36Sopenharmony_ci int len; 51162306a36Sopenharmony_ci int total_len; 51262306a36Sopenharmony_ci __wsum tmp_csum; 51362306a36Sopenharmony_ci __sum16 csum; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (!rp->checksum) 51662306a36Sopenharmony_ci goto send; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci skb = skb_peek(&sk->sk_write_queue); 51962306a36Sopenharmony_ci if (!skb) 52062306a36Sopenharmony_ci goto out; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci offset = rp->offset; 52362306a36Sopenharmony_ci total_len = inet_sk(sk)->cork.base.length; 52462306a36Sopenharmony_ci opt = inet6_sk(sk)->cork.opt; 52562306a36Sopenharmony_ci total_len -= opt ? opt->opt_flen : 0; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (offset >= total_len - 1) { 52862306a36Sopenharmony_ci err = -EINVAL; 52962306a36Sopenharmony_ci ip6_flush_pending_frames(sk); 53062306a36Sopenharmony_ci goto out; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* should be check HW csum miyazawa */ 53462306a36Sopenharmony_ci if (skb_queue_len(&sk->sk_write_queue) == 1) { 53562306a36Sopenharmony_ci /* 53662306a36Sopenharmony_ci * Only one fragment on the socket. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci tmp_csum = skb->csum; 53962306a36Sopenharmony_ci } else { 54062306a36Sopenharmony_ci struct sk_buff *csum_skb = NULL; 54162306a36Sopenharmony_ci tmp_csum = 0; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci skb_queue_walk(&sk->sk_write_queue, skb) { 54462306a36Sopenharmony_ci tmp_csum = csum_add(tmp_csum, skb->csum); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (csum_skb) 54762306a36Sopenharmony_ci continue; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci len = skb->len - skb_transport_offset(skb); 55062306a36Sopenharmony_ci if (offset >= len) { 55162306a36Sopenharmony_ci offset -= len; 55262306a36Sopenharmony_ci continue; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci csum_skb = skb; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci skb = csum_skb; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci offset += skb_transport_offset(skb); 56262306a36Sopenharmony_ci err = skb_copy_bits(skb, offset, &csum, 2); 56362306a36Sopenharmony_ci if (err < 0) { 56462306a36Sopenharmony_ci ip6_flush_pending_frames(sk); 56562306a36Sopenharmony_ci goto out; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* in case cksum was not initialized */ 56962306a36Sopenharmony_ci if (unlikely(csum)) 57062306a36Sopenharmony_ci tmp_csum = csum_sub(tmp_csum, csum_unfold(csum)); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci csum = csum_ipv6_magic(&fl6->saddr, &fl6->daddr, 57362306a36Sopenharmony_ci total_len, fl6->flowi6_proto, tmp_csum); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (csum == 0 && fl6->flowi6_proto == IPPROTO_UDP) 57662306a36Sopenharmony_ci csum = CSUM_MANGLED_0; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci BUG_ON(skb_store_bits(skb, offset, &csum, 2)); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cisend: 58162306a36Sopenharmony_ci err = ip6_push_pending_frames(sk); 58262306a36Sopenharmony_ciout: 58362306a36Sopenharmony_ci return err; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, 58762306a36Sopenharmony_ci struct flowi6 *fl6, struct dst_entry **dstp, 58862306a36Sopenharmony_ci unsigned int flags, const struct sockcm_cookie *sockc) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 59162306a36Sopenharmony_ci struct net *net = sock_net(sk); 59262306a36Sopenharmony_ci struct ipv6hdr *iph; 59362306a36Sopenharmony_ci struct sk_buff *skb; 59462306a36Sopenharmony_ci int err; 59562306a36Sopenharmony_ci struct rt6_info *rt = (struct rt6_info *)*dstp; 59662306a36Sopenharmony_ci int hlen = LL_RESERVED_SPACE(rt->dst.dev); 59762306a36Sopenharmony_ci int tlen = rt->dst.dev->needed_tailroom; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (length > rt->dst.dev->mtu) { 60062306a36Sopenharmony_ci ipv6_local_error(sk, EMSGSIZE, fl6, rt->dst.dev->mtu); 60162306a36Sopenharmony_ci return -EMSGSIZE; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci if (length < sizeof(struct ipv6hdr)) 60462306a36Sopenharmony_ci return -EINVAL; 60562306a36Sopenharmony_ci if (flags&MSG_PROBE) 60662306a36Sopenharmony_ci goto out; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci skb = sock_alloc_send_skb(sk, 60962306a36Sopenharmony_ci length + hlen + tlen + 15, 61062306a36Sopenharmony_ci flags & MSG_DONTWAIT, &err); 61162306a36Sopenharmony_ci if (!skb) 61262306a36Sopenharmony_ci goto error; 61362306a36Sopenharmony_ci skb_reserve(skb, hlen); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 61662306a36Sopenharmony_ci skb->priority = READ_ONCE(sk->sk_priority); 61762306a36Sopenharmony_ci skb->mark = sockc->mark; 61862306a36Sopenharmony_ci skb->tstamp = sockc->transmit_time; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci skb_put(skb, length); 62162306a36Sopenharmony_ci skb_reset_network_header(skb); 62262306a36Sopenharmony_ci iph = ipv6_hdr(skb); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci skb_setup_tx_timestamp(skb, sockc->tsflags); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (flags & MSG_CONFIRM) 62962306a36Sopenharmony_ci skb_set_dst_pending_confirm(skb, 1); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci skb->transport_header = skb->network_header; 63262306a36Sopenharmony_ci err = memcpy_from_msg(iph, msg, length); 63362306a36Sopenharmony_ci if (err) { 63462306a36Sopenharmony_ci err = -EFAULT; 63562306a36Sopenharmony_ci kfree_skb(skb); 63662306a36Sopenharmony_ci goto error; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci skb_dst_set(skb, &rt->dst); 64062306a36Sopenharmony_ci *dstp = NULL; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* if egress device is enslaved to an L3 master device pass the 64362306a36Sopenharmony_ci * skb to its handler for processing 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_ci skb = l3mdev_ip6_out(sk, skb); 64662306a36Sopenharmony_ci if (unlikely(!skb)) 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* Acquire rcu_read_lock() in case we need to use rt->rt6i_idev 65062306a36Sopenharmony_ci * in the error path. Since skb has been freed, the dst could 65162306a36Sopenharmony_ci * have been queued for deletion. 65262306a36Sopenharmony_ci */ 65362306a36Sopenharmony_ci rcu_read_lock(); 65462306a36Sopenharmony_ci IP6_UPD_PO_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUT, skb->len); 65562306a36Sopenharmony_ci err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb, 65662306a36Sopenharmony_ci NULL, rt->dst.dev, dst_output); 65762306a36Sopenharmony_ci if (err > 0) 65862306a36Sopenharmony_ci err = net_xmit_errno(err); 65962306a36Sopenharmony_ci if (err) { 66062306a36Sopenharmony_ci IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); 66162306a36Sopenharmony_ci rcu_read_unlock(); 66262306a36Sopenharmony_ci goto error_check; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci rcu_read_unlock(); 66562306a36Sopenharmony_ciout: 66662306a36Sopenharmony_ci return 0; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cierror: 66962306a36Sopenharmony_ci IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); 67062306a36Sopenharmony_cierror_check: 67162306a36Sopenharmony_ci if (err == -ENOBUFS && !np->recverr) 67262306a36Sopenharmony_ci err = 0; 67362306a36Sopenharmony_ci return err; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistruct raw6_frag_vec { 67762306a36Sopenharmony_ci struct msghdr *msg; 67862306a36Sopenharmony_ci int hlen; 67962306a36Sopenharmony_ci char c[4]; 68062306a36Sopenharmony_ci}; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int rawv6_probe_proto_opt(struct raw6_frag_vec *rfv, struct flowi6 *fl6) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci int err = 0; 68562306a36Sopenharmony_ci switch (fl6->flowi6_proto) { 68662306a36Sopenharmony_ci case IPPROTO_ICMPV6: 68762306a36Sopenharmony_ci rfv->hlen = 2; 68862306a36Sopenharmony_ci err = memcpy_from_msg(rfv->c, rfv->msg, rfv->hlen); 68962306a36Sopenharmony_ci if (!err) { 69062306a36Sopenharmony_ci fl6->fl6_icmp_type = rfv->c[0]; 69162306a36Sopenharmony_ci fl6->fl6_icmp_code = rfv->c[1]; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci case IPPROTO_MH: 69562306a36Sopenharmony_ci rfv->hlen = 4; 69662306a36Sopenharmony_ci err = memcpy_from_msg(rfv->c, rfv->msg, rfv->hlen); 69762306a36Sopenharmony_ci if (!err) 69862306a36Sopenharmony_ci fl6->fl6_mh_type = rfv->c[2]; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci return err; 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic int raw6_getfrag(void *from, char *to, int offset, int len, int odd, 70462306a36Sopenharmony_ci struct sk_buff *skb) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci struct raw6_frag_vec *rfv = from; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (offset < rfv->hlen) { 70962306a36Sopenharmony_ci int copy = min(rfv->hlen - offset, len); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 71262306a36Sopenharmony_ci memcpy(to, rfv->c + offset, copy); 71362306a36Sopenharmony_ci else 71462306a36Sopenharmony_ci skb->csum = csum_block_add( 71562306a36Sopenharmony_ci skb->csum, 71662306a36Sopenharmony_ci csum_partial_copy_nocheck(rfv->c + offset, 71762306a36Sopenharmony_ci to, copy), 71862306a36Sopenharmony_ci odd); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci odd = 0; 72162306a36Sopenharmony_ci offset += copy; 72262306a36Sopenharmony_ci to += copy; 72362306a36Sopenharmony_ci len -= copy; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (!len) 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci offset -= rfv->hlen; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci return ip_generic_getfrag(rfv->msg, to, offset, len, odd, skb); 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct ipv6_txoptions *opt_to_free = NULL; 73762306a36Sopenharmony_ci struct ipv6_txoptions opt_space; 73862306a36Sopenharmony_ci DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name); 73962306a36Sopenharmony_ci struct in6_addr *daddr, *final_p, final; 74062306a36Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 74162306a36Sopenharmony_ci struct ipv6_pinfo *np = inet6_sk(sk); 74262306a36Sopenharmony_ci struct raw6_sock *rp = raw6_sk(sk); 74362306a36Sopenharmony_ci struct ipv6_txoptions *opt = NULL; 74462306a36Sopenharmony_ci struct ip6_flowlabel *flowlabel = NULL; 74562306a36Sopenharmony_ci struct dst_entry *dst = NULL; 74662306a36Sopenharmony_ci struct raw6_frag_vec rfv; 74762306a36Sopenharmony_ci struct flowi6 fl6; 74862306a36Sopenharmony_ci struct ipcm6_cookie ipc6; 74962306a36Sopenharmony_ci int addr_len = msg->msg_namelen; 75062306a36Sopenharmony_ci int hdrincl; 75162306a36Sopenharmony_ci u16 proto; 75262306a36Sopenharmony_ci int err; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* Rough check on arithmetic overflow, 75562306a36Sopenharmony_ci better check is made in ip6_append_data(). 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_ci if (len > INT_MAX) 75862306a36Sopenharmony_ci return -EMSGSIZE; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci /* Mirror BSD error message compatibility */ 76162306a36Sopenharmony_ci if (msg->msg_flags & MSG_OOB) 76262306a36Sopenharmony_ci return -EOPNOTSUPP; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci hdrincl = inet_test_bit(HDRINCL, sk); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* 76762306a36Sopenharmony_ci * Get and verify the address. 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_ci memset(&fl6, 0, sizeof(fl6)); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci fl6.flowi6_mark = READ_ONCE(sk->sk_mark); 77262306a36Sopenharmony_ci fl6.flowi6_uid = sk->sk_uid; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci ipcm6_init(&ipc6); 77562306a36Sopenharmony_ci ipc6.sockc.tsflags = READ_ONCE(sk->sk_tsflags); 77662306a36Sopenharmony_ci ipc6.sockc.mark = fl6.flowi6_mark; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (sin6) { 77962306a36Sopenharmony_ci if (addr_len < SIN6_LEN_RFC2133) 78062306a36Sopenharmony_ci return -EINVAL; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (sin6->sin6_family && sin6->sin6_family != AF_INET6) 78362306a36Sopenharmony_ci return -EAFNOSUPPORT; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* port is the proto value [0..255] carried in nexthdr */ 78662306a36Sopenharmony_ci proto = ntohs(sin6->sin6_port); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci if (!proto) 78962306a36Sopenharmony_ci proto = inet->inet_num; 79062306a36Sopenharmony_ci else if (proto != inet->inet_num && 79162306a36Sopenharmony_ci inet->inet_num != IPPROTO_RAW) 79262306a36Sopenharmony_ci return -EINVAL; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (proto > 255) 79562306a36Sopenharmony_ci return -EINVAL; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci daddr = &sin6->sin6_addr; 79862306a36Sopenharmony_ci if (np->sndflow) { 79962306a36Sopenharmony_ci fl6.flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; 80062306a36Sopenharmony_ci if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) { 80162306a36Sopenharmony_ci flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); 80262306a36Sopenharmony_ci if (IS_ERR(flowlabel)) 80362306a36Sopenharmony_ci return -EINVAL; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* 80862306a36Sopenharmony_ci * Otherwise it will be difficult to maintain 80962306a36Sopenharmony_ci * sk->sk_dst_cache. 81062306a36Sopenharmony_ci */ 81162306a36Sopenharmony_ci if (sk->sk_state == TCP_ESTABLISHED && 81262306a36Sopenharmony_ci ipv6_addr_equal(daddr, &sk->sk_v6_daddr)) 81362306a36Sopenharmony_ci daddr = &sk->sk_v6_daddr; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (addr_len >= sizeof(struct sockaddr_in6) && 81662306a36Sopenharmony_ci sin6->sin6_scope_id && 81762306a36Sopenharmony_ci __ipv6_addr_needs_scope_id(__ipv6_addr_type(daddr))) 81862306a36Sopenharmony_ci fl6.flowi6_oif = sin6->sin6_scope_id; 81962306a36Sopenharmony_ci } else { 82062306a36Sopenharmony_ci if (sk->sk_state != TCP_ESTABLISHED) 82162306a36Sopenharmony_ci return -EDESTADDRREQ; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci proto = inet->inet_num; 82462306a36Sopenharmony_ci daddr = &sk->sk_v6_daddr; 82562306a36Sopenharmony_ci fl6.flowlabel = np->flow_label; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (fl6.flowi6_oif == 0) 82962306a36Sopenharmony_ci fl6.flowi6_oif = sk->sk_bound_dev_if; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci if (msg->msg_controllen) { 83262306a36Sopenharmony_ci opt = &opt_space; 83362306a36Sopenharmony_ci memset(opt, 0, sizeof(struct ipv6_txoptions)); 83462306a36Sopenharmony_ci opt->tot_len = sizeof(struct ipv6_txoptions); 83562306a36Sopenharmony_ci ipc6.opt = opt; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, &ipc6); 83862306a36Sopenharmony_ci if (err < 0) { 83962306a36Sopenharmony_ci fl6_sock_release(flowlabel); 84062306a36Sopenharmony_ci return err; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci if ((fl6.flowlabel&IPV6_FLOWLABEL_MASK) && !flowlabel) { 84362306a36Sopenharmony_ci flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); 84462306a36Sopenharmony_ci if (IS_ERR(flowlabel)) 84562306a36Sopenharmony_ci return -EINVAL; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci if (!(opt->opt_nflen|opt->opt_flen)) 84862306a36Sopenharmony_ci opt = NULL; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci if (!opt) { 85162306a36Sopenharmony_ci opt = txopt_get(np); 85262306a36Sopenharmony_ci opt_to_free = opt; 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci if (flowlabel) 85562306a36Sopenharmony_ci opt = fl6_merge_options(&opt_space, flowlabel, opt); 85662306a36Sopenharmony_ci opt = ipv6_fixup_options(&opt_space, opt); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci fl6.flowi6_proto = proto; 85962306a36Sopenharmony_ci fl6.flowi6_mark = ipc6.sockc.mark; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (!hdrincl) { 86262306a36Sopenharmony_ci rfv.msg = msg; 86362306a36Sopenharmony_ci rfv.hlen = 0; 86462306a36Sopenharmony_ci err = rawv6_probe_proto_opt(&rfv, &fl6); 86562306a36Sopenharmony_ci if (err) 86662306a36Sopenharmony_ci goto out; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (!ipv6_addr_any(daddr)) 87062306a36Sopenharmony_ci fl6.daddr = *daddr; 87162306a36Sopenharmony_ci else 87262306a36Sopenharmony_ci fl6.daddr.s6_addr[15] = 0x1; /* :: means loopback (BSD'ism) */ 87362306a36Sopenharmony_ci if (ipv6_addr_any(&fl6.saddr) && !ipv6_addr_any(&np->saddr)) 87462306a36Sopenharmony_ci fl6.saddr = np->saddr; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci final_p = fl6_update_dst(&fl6, opt, &final); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (!fl6.flowi6_oif && ipv6_addr_is_multicast(&fl6.daddr)) 87962306a36Sopenharmony_ci fl6.flowi6_oif = np->mcast_oif; 88062306a36Sopenharmony_ci else if (!fl6.flowi6_oif) 88162306a36Sopenharmony_ci fl6.flowi6_oif = np->ucast_oif; 88262306a36Sopenharmony_ci security_sk_classify_flow(sk, flowi6_to_flowi_common(&fl6)); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (hdrincl) 88562306a36Sopenharmony_ci fl6.flowi6_flags |= FLOWI_FLAG_KNOWN_NH; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci if (ipc6.tclass < 0) 88862306a36Sopenharmony_ci ipc6.tclass = np->tclass; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci fl6.flowlabel = ip6_make_flowinfo(ipc6.tclass, fl6.flowlabel); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl6, final_p); 89362306a36Sopenharmony_ci if (IS_ERR(dst)) { 89462306a36Sopenharmony_ci err = PTR_ERR(dst); 89562306a36Sopenharmony_ci goto out; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci if (ipc6.hlimit < 0) 89862306a36Sopenharmony_ci ipc6.hlimit = ip6_sk_dst_hoplimit(np, &fl6, dst); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (ipc6.dontfrag < 0) 90162306a36Sopenharmony_ci ipc6.dontfrag = np->dontfrag; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (msg->msg_flags&MSG_CONFIRM) 90462306a36Sopenharmony_ci goto do_confirm; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ciback_from_confirm: 90762306a36Sopenharmony_ci if (hdrincl) 90862306a36Sopenharmony_ci err = rawv6_send_hdrinc(sk, msg, len, &fl6, &dst, 90962306a36Sopenharmony_ci msg->msg_flags, &ipc6.sockc); 91062306a36Sopenharmony_ci else { 91162306a36Sopenharmony_ci ipc6.opt = opt; 91262306a36Sopenharmony_ci lock_sock(sk); 91362306a36Sopenharmony_ci err = ip6_append_data(sk, raw6_getfrag, &rfv, 91462306a36Sopenharmony_ci len, 0, &ipc6, &fl6, (struct rt6_info *)dst, 91562306a36Sopenharmony_ci msg->msg_flags); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (err) 91862306a36Sopenharmony_ci ip6_flush_pending_frames(sk); 91962306a36Sopenharmony_ci else if (!(msg->msg_flags & MSG_MORE)) 92062306a36Sopenharmony_ci err = rawv6_push_pending_frames(sk, &fl6, rp); 92162306a36Sopenharmony_ci release_sock(sk); 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_cidone: 92462306a36Sopenharmony_ci dst_release(dst); 92562306a36Sopenharmony_ciout: 92662306a36Sopenharmony_ci fl6_sock_release(flowlabel); 92762306a36Sopenharmony_ci txopt_put(opt_to_free); 92862306a36Sopenharmony_ci return err < 0 ? err : len; 92962306a36Sopenharmony_cido_confirm: 93062306a36Sopenharmony_ci if (msg->msg_flags & MSG_PROBE) 93162306a36Sopenharmony_ci dst_confirm_neigh(dst, &fl6.daddr); 93262306a36Sopenharmony_ci if (!(msg->msg_flags & MSG_PROBE) || len) 93362306a36Sopenharmony_ci goto back_from_confirm; 93462306a36Sopenharmony_ci err = 0; 93562306a36Sopenharmony_ci goto done; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic int rawv6_seticmpfilter(struct sock *sk, int level, int optname, 93962306a36Sopenharmony_ci sockptr_t optval, int optlen) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci switch (optname) { 94262306a36Sopenharmony_ci case ICMPV6_FILTER: 94362306a36Sopenharmony_ci if (optlen > sizeof(struct icmp6_filter)) 94462306a36Sopenharmony_ci optlen = sizeof(struct icmp6_filter); 94562306a36Sopenharmony_ci if (copy_from_sockptr(&raw6_sk(sk)->filter, optval, optlen)) 94662306a36Sopenharmony_ci return -EFAULT; 94762306a36Sopenharmony_ci return 0; 94862306a36Sopenharmony_ci default: 94962306a36Sopenharmony_ci return -ENOPROTOOPT; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci return 0; 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic int rawv6_geticmpfilter(struct sock *sk, int level, int optname, 95662306a36Sopenharmony_ci char __user *optval, int __user *optlen) 95762306a36Sopenharmony_ci{ 95862306a36Sopenharmony_ci int len; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci switch (optname) { 96162306a36Sopenharmony_ci case ICMPV6_FILTER: 96262306a36Sopenharmony_ci if (get_user(len, optlen)) 96362306a36Sopenharmony_ci return -EFAULT; 96462306a36Sopenharmony_ci if (len < 0) 96562306a36Sopenharmony_ci return -EINVAL; 96662306a36Sopenharmony_ci if (len > sizeof(struct icmp6_filter)) 96762306a36Sopenharmony_ci len = sizeof(struct icmp6_filter); 96862306a36Sopenharmony_ci if (put_user(len, optlen)) 96962306a36Sopenharmony_ci return -EFAULT; 97062306a36Sopenharmony_ci if (copy_to_user(optval, &raw6_sk(sk)->filter, len)) 97162306a36Sopenharmony_ci return -EFAULT; 97262306a36Sopenharmony_ci return 0; 97362306a36Sopenharmony_ci default: 97462306a36Sopenharmony_ci return -ENOPROTOOPT; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return 0; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic int do_rawv6_setsockopt(struct sock *sk, int level, int optname, 98262306a36Sopenharmony_ci sockptr_t optval, unsigned int optlen) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci struct raw6_sock *rp = raw6_sk(sk); 98562306a36Sopenharmony_ci int val; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (optlen < sizeof(val)) 98862306a36Sopenharmony_ci return -EINVAL; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (copy_from_sockptr(&val, optval, sizeof(val))) 99162306a36Sopenharmony_ci return -EFAULT; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci switch (optname) { 99462306a36Sopenharmony_ci case IPV6_HDRINCL: 99562306a36Sopenharmony_ci if (sk->sk_type != SOCK_RAW) 99662306a36Sopenharmony_ci return -EINVAL; 99762306a36Sopenharmony_ci inet_assign_bit(HDRINCL, sk, val); 99862306a36Sopenharmony_ci return 0; 99962306a36Sopenharmony_ci case IPV6_CHECKSUM: 100062306a36Sopenharmony_ci if (inet_sk(sk)->inet_num == IPPROTO_ICMPV6 && 100162306a36Sopenharmony_ci level == IPPROTO_IPV6) { 100262306a36Sopenharmony_ci /* 100362306a36Sopenharmony_ci * RFC3542 tells that IPV6_CHECKSUM socket 100462306a36Sopenharmony_ci * option in the IPPROTO_IPV6 level is not 100562306a36Sopenharmony_ci * allowed on ICMPv6 sockets. 100662306a36Sopenharmony_ci * If you want to set it, use IPPROTO_RAW 100762306a36Sopenharmony_ci * level IPV6_CHECKSUM socket option 100862306a36Sopenharmony_ci * (Linux extension). 100962306a36Sopenharmony_ci */ 101062306a36Sopenharmony_ci return -EINVAL; 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* You may get strange result with a positive odd offset; 101462306a36Sopenharmony_ci RFC2292bis agrees with me. */ 101562306a36Sopenharmony_ci if (val > 0 && (val&1)) 101662306a36Sopenharmony_ci return -EINVAL; 101762306a36Sopenharmony_ci if (val < 0) { 101862306a36Sopenharmony_ci rp->checksum = 0; 101962306a36Sopenharmony_ci } else { 102062306a36Sopenharmony_ci rp->checksum = 1; 102162306a36Sopenharmony_ci rp->offset = val; 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci return 0; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci default: 102762306a36Sopenharmony_ci return -ENOPROTOOPT; 102862306a36Sopenharmony_ci } 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_cistatic int rawv6_setsockopt(struct sock *sk, int level, int optname, 103262306a36Sopenharmony_ci sockptr_t optval, unsigned int optlen) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci switch (level) { 103562306a36Sopenharmony_ci case SOL_RAW: 103662306a36Sopenharmony_ci break; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci case SOL_ICMPV6: 103962306a36Sopenharmony_ci if (inet_sk(sk)->inet_num != IPPROTO_ICMPV6) 104062306a36Sopenharmony_ci return -EOPNOTSUPP; 104162306a36Sopenharmony_ci return rawv6_seticmpfilter(sk, level, optname, optval, optlen); 104262306a36Sopenharmony_ci case SOL_IPV6: 104362306a36Sopenharmony_ci if (optname == IPV6_CHECKSUM || 104462306a36Sopenharmony_ci optname == IPV6_HDRINCL) 104562306a36Sopenharmony_ci break; 104662306a36Sopenharmony_ci fallthrough; 104762306a36Sopenharmony_ci default: 104862306a36Sopenharmony_ci return ipv6_setsockopt(sk, level, optname, optval, optlen); 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci return do_rawv6_setsockopt(sk, level, optname, optval, optlen); 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_cistatic int do_rawv6_getsockopt(struct sock *sk, int level, int optname, 105562306a36Sopenharmony_ci char __user *optval, int __user *optlen) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci struct raw6_sock *rp = raw6_sk(sk); 105862306a36Sopenharmony_ci int val, len; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (get_user(len, optlen)) 106162306a36Sopenharmony_ci return -EFAULT; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci switch (optname) { 106462306a36Sopenharmony_ci case IPV6_HDRINCL: 106562306a36Sopenharmony_ci val = inet_test_bit(HDRINCL, sk); 106662306a36Sopenharmony_ci break; 106762306a36Sopenharmony_ci case IPV6_CHECKSUM: 106862306a36Sopenharmony_ci /* 106962306a36Sopenharmony_ci * We allow getsockopt() for IPPROTO_IPV6-level 107062306a36Sopenharmony_ci * IPV6_CHECKSUM socket option on ICMPv6 sockets 107162306a36Sopenharmony_ci * since RFC3542 is silent about it. 107262306a36Sopenharmony_ci */ 107362306a36Sopenharmony_ci if (rp->checksum == 0) 107462306a36Sopenharmony_ci val = -1; 107562306a36Sopenharmony_ci else 107662306a36Sopenharmony_ci val = rp->offset; 107762306a36Sopenharmony_ci break; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci default: 108062306a36Sopenharmony_ci return -ENOPROTOOPT; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci len = min_t(unsigned int, sizeof(int), len); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (put_user(len, optlen)) 108662306a36Sopenharmony_ci return -EFAULT; 108762306a36Sopenharmony_ci if (copy_to_user(optval, &val, len)) 108862306a36Sopenharmony_ci return -EFAULT; 108962306a36Sopenharmony_ci return 0; 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_cistatic int rawv6_getsockopt(struct sock *sk, int level, int optname, 109362306a36Sopenharmony_ci char __user *optval, int __user *optlen) 109462306a36Sopenharmony_ci{ 109562306a36Sopenharmony_ci switch (level) { 109662306a36Sopenharmony_ci case SOL_RAW: 109762306a36Sopenharmony_ci break; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci case SOL_ICMPV6: 110062306a36Sopenharmony_ci if (inet_sk(sk)->inet_num != IPPROTO_ICMPV6) 110162306a36Sopenharmony_ci return -EOPNOTSUPP; 110262306a36Sopenharmony_ci return rawv6_geticmpfilter(sk, level, optname, optval, optlen); 110362306a36Sopenharmony_ci case SOL_IPV6: 110462306a36Sopenharmony_ci if (optname == IPV6_CHECKSUM || 110562306a36Sopenharmony_ci optname == IPV6_HDRINCL) 110662306a36Sopenharmony_ci break; 110762306a36Sopenharmony_ci fallthrough; 110862306a36Sopenharmony_ci default: 110962306a36Sopenharmony_ci return ipv6_getsockopt(sk, level, optname, optval, optlen); 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci return do_rawv6_getsockopt(sk, level, optname, optval, optlen); 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic int rawv6_ioctl(struct sock *sk, int cmd, int *karg) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci switch (cmd) { 111862306a36Sopenharmony_ci case SIOCOUTQ: { 111962306a36Sopenharmony_ci *karg = sk_wmem_alloc_get(sk); 112062306a36Sopenharmony_ci return 0; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci case SIOCINQ: { 112362306a36Sopenharmony_ci struct sk_buff *skb; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci spin_lock_bh(&sk->sk_receive_queue.lock); 112662306a36Sopenharmony_ci skb = skb_peek(&sk->sk_receive_queue); 112762306a36Sopenharmony_ci if (skb) 112862306a36Sopenharmony_ci *karg = skb->len; 112962306a36Sopenharmony_ci else 113062306a36Sopenharmony_ci *karg = 0; 113162306a36Sopenharmony_ci spin_unlock_bh(&sk->sk_receive_queue.lock); 113262306a36Sopenharmony_ci return 0; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci default: 113662306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 113762306a36Sopenharmony_ci return ip6mr_ioctl(sk, cmd, karg); 113862306a36Sopenharmony_ci#else 113962306a36Sopenharmony_ci return -ENOIOCTLCMD; 114062306a36Sopenharmony_ci#endif 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 114562306a36Sopenharmony_cistatic int compat_rawv6_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci switch (cmd) { 114862306a36Sopenharmony_ci case SIOCOUTQ: 114962306a36Sopenharmony_ci case SIOCINQ: 115062306a36Sopenharmony_ci return -ENOIOCTLCMD; 115162306a36Sopenharmony_ci default: 115262306a36Sopenharmony_ci#ifdef CONFIG_IPV6_MROUTE 115362306a36Sopenharmony_ci return ip6mr_compat_ioctl(sk, cmd, compat_ptr(arg)); 115462306a36Sopenharmony_ci#else 115562306a36Sopenharmony_ci return -ENOIOCTLCMD; 115662306a36Sopenharmony_ci#endif 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci} 115962306a36Sopenharmony_ci#endif 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cistatic void rawv6_close(struct sock *sk, long timeout) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci if (inet_sk(sk)->inet_num == IPPROTO_RAW) 116462306a36Sopenharmony_ci ip6_ra_control(sk, -1); 116562306a36Sopenharmony_ci ip6mr_sk_done(sk); 116662306a36Sopenharmony_ci sk_common_release(sk); 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic void raw6_destroy(struct sock *sk) 117062306a36Sopenharmony_ci{ 117162306a36Sopenharmony_ci lock_sock(sk); 117262306a36Sopenharmony_ci ip6_flush_pending_frames(sk); 117362306a36Sopenharmony_ci release_sock(sk); 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic int rawv6_init_sk(struct sock *sk) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci struct raw6_sock *rp = raw6_sk(sk); 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci switch (inet_sk(sk)->inet_num) { 118162306a36Sopenharmony_ci case IPPROTO_ICMPV6: 118262306a36Sopenharmony_ci rp->checksum = 1; 118362306a36Sopenharmony_ci rp->offset = 2; 118462306a36Sopenharmony_ci break; 118562306a36Sopenharmony_ci case IPPROTO_MH: 118662306a36Sopenharmony_ci rp->checksum = 1; 118762306a36Sopenharmony_ci rp->offset = 4; 118862306a36Sopenharmony_ci break; 118962306a36Sopenharmony_ci default: 119062306a36Sopenharmony_ci break; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci return 0; 119362306a36Sopenharmony_ci} 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_cistruct proto rawv6_prot = { 119662306a36Sopenharmony_ci .name = "RAWv6", 119762306a36Sopenharmony_ci .owner = THIS_MODULE, 119862306a36Sopenharmony_ci .close = rawv6_close, 119962306a36Sopenharmony_ci .destroy = raw6_destroy, 120062306a36Sopenharmony_ci .connect = ip6_datagram_connect_v6_only, 120162306a36Sopenharmony_ci .disconnect = __udp_disconnect, 120262306a36Sopenharmony_ci .ioctl = rawv6_ioctl, 120362306a36Sopenharmony_ci .init = rawv6_init_sk, 120462306a36Sopenharmony_ci .setsockopt = rawv6_setsockopt, 120562306a36Sopenharmony_ci .getsockopt = rawv6_getsockopt, 120662306a36Sopenharmony_ci .sendmsg = rawv6_sendmsg, 120762306a36Sopenharmony_ci .recvmsg = rawv6_recvmsg, 120862306a36Sopenharmony_ci .bind = rawv6_bind, 120962306a36Sopenharmony_ci .backlog_rcv = rawv6_rcv_skb, 121062306a36Sopenharmony_ci .hash = raw_hash_sk, 121162306a36Sopenharmony_ci .unhash = raw_unhash_sk, 121262306a36Sopenharmony_ci .obj_size = sizeof(struct raw6_sock), 121362306a36Sopenharmony_ci .ipv6_pinfo_offset = offsetof(struct raw6_sock, inet6), 121462306a36Sopenharmony_ci .useroffset = offsetof(struct raw6_sock, filter), 121562306a36Sopenharmony_ci .usersize = sizeof_field(struct raw6_sock, filter), 121662306a36Sopenharmony_ci .h.raw_hash = &raw_v6_hashinfo, 121762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 121862306a36Sopenharmony_ci .compat_ioctl = compat_rawv6_ioctl, 121962306a36Sopenharmony_ci#endif 122062306a36Sopenharmony_ci .diag_destroy = raw_abort, 122162306a36Sopenharmony_ci}; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 122462306a36Sopenharmony_cistatic int raw6_seq_show(struct seq_file *seq, void *v) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) { 122762306a36Sopenharmony_ci seq_puts(seq, IPV6_SEQ_DGRAM_HEADER); 122862306a36Sopenharmony_ci } else { 122962306a36Sopenharmony_ci struct sock *sp = v; 123062306a36Sopenharmony_ci __u16 srcp = inet_sk(sp)->inet_num; 123162306a36Sopenharmony_ci ip6_dgram_sock_seq_show(seq, v, srcp, 0, 123262306a36Sopenharmony_ci raw_seq_private(seq)->bucket); 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci return 0; 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cistatic const struct seq_operations raw6_seq_ops = { 123862306a36Sopenharmony_ci .start = raw_seq_start, 123962306a36Sopenharmony_ci .next = raw_seq_next, 124062306a36Sopenharmony_ci .stop = raw_seq_stop, 124162306a36Sopenharmony_ci .show = raw6_seq_show, 124262306a36Sopenharmony_ci}; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic int __net_init raw6_init_net(struct net *net) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci if (!proc_create_net_data("raw6", 0444, net->proc_net, &raw6_seq_ops, 124762306a36Sopenharmony_ci sizeof(struct raw_iter_state), &raw_v6_hashinfo)) 124862306a36Sopenharmony_ci return -ENOMEM; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci return 0; 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_cistatic void __net_exit raw6_exit_net(struct net *net) 125462306a36Sopenharmony_ci{ 125562306a36Sopenharmony_ci remove_proc_entry("raw6", net->proc_net); 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic struct pernet_operations raw6_net_ops = { 125962306a36Sopenharmony_ci .init = raw6_init_net, 126062306a36Sopenharmony_ci .exit = raw6_exit_net, 126162306a36Sopenharmony_ci}; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ciint __init raw6_proc_init(void) 126462306a36Sopenharmony_ci{ 126562306a36Sopenharmony_ci return register_pernet_subsys(&raw6_net_ops); 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_civoid raw6_proc_exit(void) 126962306a36Sopenharmony_ci{ 127062306a36Sopenharmony_ci unregister_pernet_subsys(&raw6_net_ops); 127162306a36Sopenharmony_ci} 127262306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci/* Same as inet6_dgram_ops, sans udp_poll. */ 127562306a36Sopenharmony_ciconst struct proto_ops inet6_sockraw_ops = { 127662306a36Sopenharmony_ci .family = PF_INET6, 127762306a36Sopenharmony_ci .owner = THIS_MODULE, 127862306a36Sopenharmony_ci .release = inet6_release, 127962306a36Sopenharmony_ci .bind = inet6_bind, 128062306a36Sopenharmony_ci .connect = inet_dgram_connect, /* ok */ 128162306a36Sopenharmony_ci .socketpair = sock_no_socketpair, /* a do nothing */ 128262306a36Sopenharmony_ci .accept = sock_no_accept, /* a do nothing */ 128362306a36Sopenharmony_ci .getname = inet6_getname, 128462306a36Sopenharmony_ci .poll = datagram_poll, /* ok */ 128562306a36Sopenharmony_ci .ioctl = inet6_ioctl, /* must change */ 128662306a36Sopenharmony_ci .gettstamp = sock_gettstamp, 128762306a36Sopenharmony_ci .listen = sock_no_listen, /* ok */ 128862306a36Sopenharmony_ci .shutdown = inet_shutdown, /* ok */ 128962306a36Sopenharmony_ci .setsockopt = sock_common_setsockopt, /* ok */ 129062306a36Sopenharmony_ci .getsockopt = sock_common_getsockopt, /* ok */ 129162306a36Sopenharmony_ci .sendmsg = inet_sendmsg, /* ok */ 129262306a36Sopenharmony_ci .recvmsg = sock_common_recvmsg, /* ok */ 129362306a36Sopenharmony_ci .mmap = sock_no_mmap, 129462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 129562306a36Sopenharmony_ci .compat_ioctl = inet6_compat_ioctl, 129662306a36Sopenharmony_ci#endif 129762306a36Sopenharmony_ci}; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_cistatic struct inet_protosw rawv6_protosw = { 130062306a36Sopenharmony_ci .type = SOCK_RAW, 130162306a36Sopenharmony_ci .protocol = IPPROTO_IP, /* wild card */ 130262306a36Sopenharmony_ci .prot = &rawv6_prot, 130362306a36Sopenharmony_ci .ops = &inet6_sockraw_ops, 130462306a36Sopenharmony_ci .flags = INET_PROTOSW_REUSE, 130562306a36Sopenharmony_ci}; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ciint __init rawv6_init(void) 130862306a36Sopenharmony_ci{ 130962306a36Sopenharmony_ci return inet6_register_protosw(&rawv6_protosw); 131062306a36Sopenharmony_ci} 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_civoid rawv6_exit(void) 131362306a36Sopenharmony_ci{ 131462306a36Sopenharmony_ci inet6_unregister_protosw(&rawv6_protosw); 131562306a36Sopenharmony_ci} 1316