1 /* Copyright (c) 2014 Mahesh Bandewar <maheshb@google.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
11 static unsigned int ipvlan_netid __read_mostly
;
14 unsigned int ipvl_nf_hook_refcnt
;
17 static struct ipvl_addr
*ipvlan_skb_to_addr(struct sk_buff
*skb
,
18 struct net_device
*dev
)
20 struct ipvl_addr
*addr
= NULL
;
21 struct ipvl_port
*port
;
25 if (!dev
|| !netif_is_ipvlan_port(dev
))
28 port
= ipvlan_port_get_rcu(dev
);
29 if (!port
|| port
->mode
!= IPVLAN_MODE_L3S
)
32 lyr3h
= ipvlan_get_L3_hdr(port
, skb
, &addr_type
);
36 addr
= ipvlan_addr_lookup(port
, lyr3h
, addr_type
, true);
41 static struct sk_buff
*ipvlan_l3_rcv(struct net_device
*dev
,
42 struct sk_buff
*skb
, u16 proto
)
44 struct ipvl_addr
*addr
;
45 struct net_device
*sdev
;
47 addr
= ipvlan_skb_to_addr(skb
, dev
);
51 sdev
= addr
->master
->dev
;
55 struct iphdr
*ip4h
= ip_hdr(skb
);
58 err
= ip_route_input_noref(skb
, ip4h
->daddr
, ip4h
->saddr
,
64 #if IS_ENABLED(CONFIG_IPV6)
67 struct dst_entry
*dst
;
68 struct ipv6hdr
*ip6h
= ipv6_hdr(skb
);
69 int flags
= RT6_LOOKUP_F_HAS_SADDR
;
71 .flowi6_iif
= sdev
->ifindex
,
74 .flowlabel
= ip6_flowinfo(ip6h
),
75 .flowi6_mark
= skb
->mark
,
76 .flowi6_proto
= ip6h
->nexthdr
,
80 dst
= ip6_route_input_lookup(dev_net(sdev
), sdev
, &fl6
,
82 skb_dst_set(skb
, dst
);
93 static const struct l3mdev_ops ipvl_l3mdev_ops
= {
94 .l3mdev_l3_rcv
= ipvlan_l3_rcv
,
97 static unsigned int ipvlan_nf_input(void *priv
, struct sk_buff
*skb
,
98 const struct nf_hook_state
*state
)
100 struct ipvl_addr
*addr
;
103 addr
= ipvlan_skb_to_addr(skb
, skb
->dev
);
107 skb
->dev
= addr
->master
->dev
;
108 len
= skb
->len
+ ETH_HLEN
;
109 ipvlan_count_rx(addr
->master
, len
, true, false);
114 static const struct nf_hook_ops ipvl_nfops
[] = {
116 .hook
= ipvlan_nf_input
,
118 .hooknum
= NF_INET_LOCAL_IN
,
121 #if IS_ENABLED(CONFIG_IPV6)
123 .hook
= ipvlan_nf_input
,
125 .hooknum
= NF_INET_LOCAL_IN
,
131 static int ipvlan_register_nf_hook(struct net
*net
)
133 struct ipvlan_netns
*vnet
= net_generic(net
, ipvlan_netid
);
136 if (!vnet
->ipvl_nf_hook_refcnt
) {
137 err
= nf_register_net_hooks(net
, ipvl_nfops
,
138 ARRAY_SIZE(ipvl_nfops
));
140 vnet
->ipvl_nf_hook_refcnt
= 1;
142 vnet
->ipvl_nf_hook_refcnt
++;
148 static void ipvlan_unregister_nf_hook(struct net
*net
)
150 struct ipvlan_netns
*vnet
= net_generic(net
, ipvlan_netid
);
152 if (WARN_ON(!vnet
->ipvl_nf_hook_refcnt
))
155 vnet
->ipvl_nf_hook_refcnt
--;
156 if (!vnet
->ipvl_nf_hook_refcnt
)
157 nf_unregister_net_hooks(net
, ipvl_nfops
,
158 ARRAY_SIZE(ipvl_nfops
));
161 void ipvlan_migrate_l3s_hook(struct net
*oldnet
, struct net
*newnet
)
163 struct ipvlan_netns
*old_vnet
;
167 old_vnet
= net_generic(oldnet
, ipvlan_netid
);
168 if (!old_vnet
->ipvl_nf_hook_refcnt
)
171 ipvlan_register_nf_hook(newnet
);
172 ipvlan_unregister_nf_hook(oldnet
);
175 static void ipvlan_ns_exit(struct net
*net
)
177 struct ipvlan_netns
*vnet
= net_generic(net
, ipvlan_netid
);
179 if (WARN_ON_ONCE(vnet
->ipvl_nf_hook_refcnt
)) {
180 vnet
->ipvl_nf_hook_refcnt
= 0;
181 nf_unregister_net_hooks(net
, ipvl_nfops
,
182 ARRAY_SIZE(ipvl_nfops
));
186 static struct pernet_operations ipvlan_net_ops
= {
188 .size
= sizeof(struct ipvlan_netns
),
189 .exit
= ipvlan_ns_exit
,
192 int ipvlan_l3s_init(void)
194 return register_pernet_subsys(&ipvlan_net_ops
);
197 void ipvlan_l3s_cleanup(void)
199 unregister_pernet_subsys(&ipvlan_net_ops
);
202 int ipvlan_l3s_register(struct ipvl_port
*port
)
204 struct net_device
*dev
= port
->dev
;
209 ret
= ipvlan_register_nf_hook(read_pnet(&port
->pnet
));
211 dev
->l3mdev_ops
= &ipvl_l3mdev_ops
;
212 dev
->priv_flags
|= IFF_L3MDEV_RX_HANDLER
;
218 void ipvlan_l3s_unregister(struct ipvl_port
*port
)
220 struct net_device
*dev
= port
->dev
;
224 dev
->priv_flags
&= ~IFF_L3MDEV_RX_HANDLER
;
225 ipvlan_unregister_nf_hook(read_pnet(&port
->pnet
));
226 dev
->l3mdev_ops
= NULL
;