229 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* 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#endif82 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 skb->skb_iif = skb->dev->ifindex;105#if IS_ENABLED(CONFIG_IPV6)106 if (addr->atype == IPVL_IPV6)107 IP6CB(skb)->iif = skb->dev->ifindex;108#endif109 len = skb->len + ETH_HLEN;110 ipvlan_count_rx(addr->master, len, true, false);111out:112 return NF_ACCEPT;113}114 115static const struct nf_hook_ops ipvl_nfops[] = {116 {117 .hook = ipvlan_nf_input,118 .pf = NFPROTO_IPV4,119 .hooknum = NF_INET_LOCAL_IN,120 .priority = INT_MAX,121 },122#if IS_ENABLED(CONFIG_IPV6)123 {124 .hook = ipvlan_nf_input,125 .pf = NFPROTO_IPV6,126 .hooknum = NF_INET_LOCAL_IN,127 .priority = INT_MAX,128 },129#endif130};131 132static int ipvlan_register_nf_hook(struct net *net)133{134 struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);135 int err = 0;136 137 if (!vnet->ipvl_nf_hook_refcnt) {138 err = nf_register_net_hooks(net, ipvl_nfops,139 ARRAY_SIZE(ipvl_nfops));140 if (!err)141 vnet->ipvl_nf_hook_refcnt = 1;142 } else {143 vnet->ipvl_nf_hook_refcnt++;144 }145 146 return err;147}148 149static void ipvlan_unregister_nf_hook(struct net *net)150{151 struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);152 153 if (WARN_ON(!vnet->ipvl_nf_hook_refcnt))154 return;155 156 vnet->ipvl_nf_hook_refcnt--;157 if (!vnet->ipvl_nf_hook_refcnt)158 nf_unregister_net_hooks(net, ipvl_nfops,159 ARRAY_SIZE(ipvl_nfops));160}161 162void ipvlan_migrate_l3s_hook(struct net *oldnet, struct net *newnet)163{164 struct ipvlan_netns *old_vnet;165 166 ASSERT_RTNL();167 168 old_vnet = net_generic(oldnet, ipvlan_netid);169 if (!old_vnet->ipvl_nf_hook_refcnt)170 return;171 172 ipvlan_register_nf_hook(newnet);173 ipvlan_unregister_nf_hook(oldnet);174}175 176static void ipvlan_ns_exit(struct net *net)177{178 struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);179 180 if (WARN_ON_ONCE(vnet->ipvl_nf_hook_refcnt)) {181 vnet->ipvl_nf_hook_refcnt = 0;182 nf_unregister_net_hooks(net, ipvl_nfops,183 ARRAY_SIZE(ipvl_nfops));184 }185}186 187static struct pernet_operations ipvlan_net_ops = {188 .id = &ipvlan_netid,189 .size = sizeof(struct ipvlan_netns),190 .exit = ipvlan_ns_exit,191};192 193int ipvlan_l3s_init(void)194{195 return register_pernet_subsys(&ipvlan_net_ops);196}197 198void ipvlan_l3s_cleanup(void)199{200 unregister_pernet_subsys(&ipvlan_net_ops);201}202 203int ipvlan_l3s_register(struct ipvl_port *port)204{205 struct net_device *dev = port->dev;206 int ret;207 208 ASSERT_RTNL();209 210 ret = ipvlan_register_nf_hook(read_pnet(&port->pnet));211 if (!ret) {212 dev->l3mdev_ops = &ipvl_l3mdev_ops;213 dev->priv_flags |= IFF_L3MDEV_RX_HANDLER;214 }215 216 return ret;217}218 219void ipvlan_l3s_unregister(struct ipvl_port *port)220{221 struct net_device *dev = port->dev;222 223 ASSERT_RTNL();224 225 dev->priv_flags &= ~IFF_L3MDEV_RX_HANDLER;226 ipvlan_unregister_nf_hook(read_pnet(&port->pnet));227 dev->l3mdev_ops = NULL;228}229