1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * IPv6 library code, needed by static components when full IPv6 support is 4 * not configured or static. These functions are needed by GSO/GRO implementation. 5 */ 6 #include <linux/export.h> 7 #include <net/ip.h> 8 #include <net/ipv6.h> 9 #include <net/ip6_fib.h> 10 #include <net/addrconf.h> 11 #include <net/secure_seq.h> 12 #include <linux/netfilter.h> 13 14 static u32 __ipv6_select_ident(struct net *net, 15 const struct in6_addr *dst, 16 const struct in6_addr *src) 17 { 18 u32 id; 19 20 do { 21 id = get_random_u32(); 22 } while (!id); 23 24 return id; 25 } 26 27 /* This function exists only for tap drivers that must support broken 28 * clients requesting UFO without specifying an IPv6 fragment ID. 29 * 30 * This is similar to ipv6_select_ident() but we use an independent hash 31 * seed to limit information leakage. 32 * 33 * The network header must be set before calling this. 34 */ 35 __be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb) 36 { 37 struct in6_addr buf[2]; 38 struct in6_addr *addrs; 39 u32 id; 40 41 addrs = skb_header_pointer(skb, 42 skb_network_offset(skb) + 43 offsetof(struct ipv6hdr, saddr), 44 sizeof(buf), buf); 45 if (!addrs) 46 return 0; 47 48 id = __ipv6_select_ident(net, &addrs[1], &addrs[0]); 49 return htonl(id); 50 } 51 EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident); 52 53 __be32 ipv6_select_ident(struct net *net, 54 const struct in6_addr *daddr, 55 const struct in6_addr *saddr) 56 { 57 u32 id; 58 59 id = __ipv6_select_ident(net, daddr, saddr); 60 return htonl(id); 61 } 62 EXPORT_SYMBOL(ipv6_select_ident); 63 64 int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) 65 { 66 unsigned int offset = sizeof(struct ipv6hdr); 67 unsigned int packet_len = skb_tail_pointer(skb) - 68 skb_network_header(skb); 69 int found_rhdr = 0; 70 *nexthdr = &ipv6_hdr(skb)->nexthdr; 71 72 while (offset <= packet_len) { 73 struct ipv6_opt_hdr *exthdr; 74 75 switch (**nexthdr) { 76 77 case NEXTHDR_HOP: 78 break; 79 case NEXTHDR_ROUTING: 80 found_rhdr = 1; 81 break; 82 case NEXTHDR_DEST: 83 #if IS_ENABLED(CONFIG_IPV6_MIP6) 84 if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) 85 break; 86 #endif 87 if (found_rhdr) 88 return offset; 89 break; 90 default: 91 return offset; 92 } 93 94 if (offset + sizeof(struct ipv6_opt_hdr) > packet_len) 95 return -EINVAL; 96 97 exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) + 98 offset); 99 offset += ipv6_optlen(exthdr); 100 if (offset > IPV6_MAXPLEN) 101 return -EINVAL; 102 *nexthdr = &exthdr->nexthdr; 103 } 104 105 return -EINVAL; 106 } 107 EXPORT_SYMBOL(ip6_find_1stfragopt); 108 109 #if IS_ENABLED(CONFIG_IPV6) 110 int ip6_dst_hoplimit(struct dst_entry *dst) 111 { 112 int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT); 113 if (hoplimit == 0) { 114 struct net_device *dev = dst->dev; 115 struct inet6_dev *idev; 116 117 rcu_read_lock(); 118 idev = __in6_dev_get(dev); 119 if (idev) 120 hoplimit = idev->cnf.hop_limit; 121 else 122 hoplimit = dev_net(dev)->ipv6.devconf_all->hop_limit; 123 rcu_read_unlock(); 124 } 125 return hoplimit; 126 } 127 EXPORT_SYMBOL(ip6_dst_hoplimit); 128 #endif 129 130 int __ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) 131 { 132 int len; 133 134 len = skb->len - sizeof(struct ipv6hdr); 135 if (len > IPV6_MAXPLEN) 136 len = 0; 137 ipv6_hdr(skb)->payload_len = htons(len); 138 IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr); 139 140 /* if egress device is enslaved to an L3 master device pass the 141 * skb to its handler for processing 142 */ 143 skb = l3mdev_ip6_out(sk, skb); 144 if (unlikely(!skb)) 145 return 0; 146 147 skb->protocol = htons(ETH_P_IPV6); 148 149 return nf_hook(NFPROTO_IPV6, NF_INET_LOCAL_OUT, 150 net, sk, skb, NULL, skb_dst(skb)->dev, 151 dst_output); 152 } 153 EXPORT_SYMBOL_GPL(__ip6_local_out); 154 155 int ip6_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) 156 { 157 int err; 158 159 err = __ip6_local_out(net, sk, skb); 160 if (likely(err == 1)) 161 err = dst_output(net, sk, skb); 162 163 return err; 164 } 165 EXPORT_SYMBOL_GPL(ip6_local_out); 166