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