Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.17-rc3 229 lines 4.7 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com> 3 */ 4 5#include <net/ip.h> 6 7#include "ipvlan.h" 8 9static unsigned int ipvlan_netid __read_mostly; 10 11struct ipvlan_netns { 12 unsigned int ipvl_nf_hook_refcnt; 13}; 14 15static struct ipvl_addr *ipvlan_skb_to_addr(struct sk_buff *skb, 16 struct net_device *dev) 17{ 18 struct ipvl_addr *addr = NULL; 19 struct ipvl_port *port; 20 int addr_type; 21 void *lyr3h; 22 23 if (!dev || !netif_is_ipvlan_port(dev)) 24 goto out; 25 26 port = ipvlan_port_get_rcu(dev); 27 if (!port || port->mode != IPVLAN_MODE_L3S) 28 goto out; 29 30 lyr3h = ipvlan_get_L3_hdr(port, skb, &addr_type); 31 if (!lyr3h) 32 goto out; 33 34 addr = ipvlan_addr_lookup(port, lyr3h, addr_type, true); 35out: 36 return addr; 37} 38 39static struct sk_buff *ipvlan_l3_rcv(struct net_device *dev, 40 struct sk_buff *skb, u16 proto) 41{ 42 struct ipvl_addr *addr; 43 struct net_device *sdev; 44 45 addr = ipvlan_skb_to_addr(skb, dev); 46 if (!addr) 47 goto out; 48 49 sdev = addr->master->dev; 50 switch (proto) { 51 case AF_INET: 52 { 53 const struct iphdr *ip4h = ip_hdr(skb); 54 int err; 55 56 err = ip_route_input_noref(skb, ip4h->daddr, ip4h->saddr, 57 ip4h_dscp(ip4h), sdev); 58 if (unlikely(err)) 59 goto out; 60 break; 61 } 62#if IS_ENABLED(CONFIG_IPV6) 63 case AF_INET6: 64 { 65 struct dst_entry *dst; 66 struct ipv6hdr *ip6h = ipv6_hdr(skb); 67 int flags = RT6_LOOKUP_F_HAS_SADDR; 68 struct flowi6 fl6 = { 69 .flowi6_iif = sdev->ifindex, 70 .daddr = ip6h->daddr, 71 .saddr = ip6h->saddr, 72 .flowlabel = ip6_flowinfo(ip6h), 73 .flowi6_mark = skb->mark, 74 .flowi6_proto = ip6h->nexthdr, 75 }; 76 77 skb_dst_drop(skb); 78 dst = ip6_route_input_lookup(dev_net(sdev), sdev, &fl6, 79 skb, flags); 80 skb_dst_set(skb, dst); 81 break; 82 } 83#endif 84 default: 85 break; 86 } 87out: 88 return skb; 89} 90 91static const struct l3mdev_ops ipvl_l3mdev_ops = { 92 .l3mdev_l3_rcv = ipvlan_l3_rcv, 93}; 94 95static unsigned int ipvlan_nf_input(void *priv, struct sk_buff *skb, 96 const struct nf_hook_state *state) 97{ 98 struct ipvl_addr *addr; 99 unsigned int len; 100 101 addr = ipvlan_skb_to_addr(skb, skb->dev); 102 if (!addr) 103 goto out; 104 105 skb->dev = addr->master->dev; 106 skb->skb_iif = skb->dev->ifindex; 107#if IS_ENABLED(CONFIG_IPV6) 108 if (addr->atype == IPVL_IPV6) 109 IP6CB(skb)->iif = skb->dev->ifindex; 110#endif 111 len = skb->len + ETH_HLEN; 112 ipvlan_count_rx(addr->master, len, true, false); 113out: 114 return NF_ACCEPT; 115} 116 117static const struct nf_hook_ops ipvl_nfops[] = { 118 { 119 .hook = ipvlan_nf_input, 120 .pf = NFPROTO_IPV4, 121 .hooknum = NF_INET_LOCAL_IN, 122 .priority = INT_MAX, 123 }, 124#if IS_ENABLED(CONFIG_IPV6) 125 { 126 .hook = ipvlan_nf_input, 127 .pf = NFPROTO_IPV6, 128 .hooknum = NF_INET_LOCAL_IN, 129 .priority = INT_MAX, 130 }, 131#endif 132}; 133 134static int ipvlan_register_nf_hook(struct net *net) 135{ 136 struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid); 137 int err = 0; 138 139 if (!vnet->ipvl_nf_hook_refcnt) { 140 err = nf_register_net_hooks(net, ipvl_nfops, 141 ARRAY_SIZE(ipvl_nfops)); 142 if (!err) 143 vnet->ipvl_nf_hook_refcnt = 1; 144 } else { 145 vnet->ipvl_nf_hook_refcnt++; 146 } 147 148 return err; 149} 150 151static void ipvlan_unregister_nf_hook(struct net *net) 152{ 153 struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid); 154 155 if (WARN_ON(!vnet->ipvl_nf_hook_refcnt)) 156 return; 157 158 vnet->ipvl_nf_hook_refcnt--; 159 if (!vnet->ipvl_nf_hook_refcnt) 160 nf_unregister_net_hooks(net, ipvl_nfops, 161 ARRAY_SIZE(ipvl_nfops)); 162} 163 164void ipvlan_migrate_l3s_hook(struct net *oldnet, struct net *newnet) 165{ 166 struct ipvlan_netns *old_vnet; 167 168 ASSERT_RTNL(); 169 170 old_vnet = net_generic(oldnet, ipvlan_netid); 171 if (!old_vnet->ipvl_nf_hook_refcnt) 172 return; 173 174 ipvlan_register_nf_hook(newnet); 175 ipvlan_unregister_nf_hook(oldnet); 176} 177 178static void ipvlan_ns_exit(struct net *net) 179{ 180 struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid); 181 182 if (WARN_ON_ONCE(vnet->ipvl_nf_hook_refcnt)) { 183 vnet->ipvl_nf_hook_refcnt = 0; 184 nf_unregister_net_hooks(net, ipvl_nfops, 185 ARRAY_SIZE(ipvl_nfops)); 186 } 187} 188 189static struct pernet_operations ipvlan_net_ops = { 190 .id = &ipvlan_netid, 191 .size = sizeof(struct ipvlan_netns), 192 .exit = ipvlan_ns_exit, 193}; 194 195int ipvlan_l3s_init(void) 196{ 197 return register_pernet_subsys(&ipvlan_net_ops); 198} 199 200void ipvlan_l3s_cleanup(void) 201{ 202 unregister_pernet_subsys(&ipvlan_net_ops); 203} 204 205int ipvlan_l3s_register(struct ipvl_port *port) 206{ 207 struct net_device *dev = port->dev; 208 int ret; 209 210 ASSERT_RTNL(); 211 212 ret = ipvlan_register_nf_hook(read_pnet(&port->pnet)); 213 if (!ret) { 214 dev->l3mdev_ops = &ipvl_l3mdev_ops; 215 dev->priv_flags |= IFF_L3MDEV_RX_HANDLER; 216 } 217 218 return ret; 219} 220 221void ipvlan_l3s_unregister(struct ipvl_port *port) 222{ 223 struct net_device *dev = port->dev; 224 225 ASSERT_RTNL(); 226 227 dev->priv_flags &= ~IFF_L3MDEV_RX_HANDLER; 228 ipvlan_unregister_nf_hook(read_pnet(&port->pnet)); 229}