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