1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | #include <linux/kernel.h> |
3 | #include <linux/init.h> |
4 | #include <linux/module.h> |
5 | #include <linux/netfilter.h> |
6 | #include <linux/rhashtable.h> |
7 | #include <net/netfilter/nf_flow_table.h> |
8 | #include <net/netfilter/nf_tables.h> |
9 | #include <linux/if_vlan.h> |
10 | |
11 | static unsigned int |
12 | nf_flow_offload_inet_hook(void *priv, struct sk_buff *skb, |
13 | const struct nf_hook_state *state) |
14 | { |
15 | struct vlan_ethhdr *veth; |
16 | __be16 proto; |
17 | |
18 | switch (skb->protocol) { |
19 | case htons(ETH_P_8021Q): |
20 | veth = (struct vlan_ethhdr *)skb_mac_header(skb); |
21 | proto = veth->h_vlan_encapsulated_proto; |
22 | break; |
23 | case htons(ETH_P_PPP_SES): |
24 | proto = nf_flow_pppoe_proto(skb); |
25 | break; |
26 | default: |
27 | proto = skb->protocol; |
28 | break; |
29 | } |
30 | |
31 | switch (proto) { |
32 | case htons(ETH_P_IP): |
33 | return nf_flow_offload_ip_hook(priv, skb, state); |
34 | case htons(ETH_P_IPV6): |
35 | return nf_flow_offload_ipv6_hook(priv, skb, state); |
36 | } |
37 | |
38 | return NF_ACCEPT; |
39 | } |
40 | |
41 | static int nf_flow_rule_route_inet(struct net *net, |
42 | struct flow_offload *flow, |
43 | enum flow_offload_tuple_dir dir, |
44 | struct nf_flow_rule *flow_rule) |
45 | { |
46 | const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple; |
47 | int err; |
48 | |
49 | switch (flow_tuple->l3proto) { |
50 | case NFPROTO_IPV4: |
51 | err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule); |
52 | break; |
53 | case NFPROTO_IPV6: |
54 | err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule); |
55 | break; |
56 | default: |
57 | err = -1; |
58 | break; |
59 | } |
60 | |
61 | return err; |
62 | } |
63 | |
64 | static struct nf_flowtable_type flowtable_inet = { |
65 | .family = NFPROTO_INET, |
66 | .init = nf_flow_table_init, |
67 | .setup = nf_flow_table_offload_setup, |
68 | .action = nf_flow_rule_route_inet, |
69 | .free = nf_flow_table_free, |
70 | .hook = nf_flow_offload_inet_hook, |
71 | .owner = THIS_MODULE, |
72 | }; |
73 | |
74 | static struct nf_flowtable_type flowtable_ipv4 = { |
75 | .family = NFPROTO_IPV4, |
76 | .init = nf_flow_table_init, |
77 | .setup = nf_flow_table_offload_setup, |
78 | .action = nf_flow_rule_route_ipv4, |
79 | .free = nf_flow_table_free, |
80 | .hook = nf_flow_offload_ip_hook, |
81 | .owner = THIS_MODULE, |
82 | }; |
83 | |
84 | static struct nf_flowtable_type flowtable_ipv6 = { |
85 | .family = NFPROTO_IPV6, |
86 | .init = nf_flow_table_init, |
87 | .setup = nf_flow_table_offload_setup, |
88 | .action = nf_flow_rule_route_ipv6, |
89 | .free = nf_flow_table_free, |
90 | .hook = nf_flow_offload_ipv6_hook, |
91 | .owner = THIS_MODULE, |
92 | }; |
93 | |
94 | static int __init nf_flow_inet_module_init(void) |
95 | { |
96 | nft_register_flowtable_type(type: &flowtable_ipv4); |
97 | nft_register_flowtable_type(type: &flowtable_ipv6); |
98 | nft_register_flowtable_type(type: &flowtable_inet); |
99 | |
100 | return 0; |
101 | } |
102 | |
103 | static void __exit nf_flow_inet_module_exit(void) |
104 | { |
105 | nft_unregister_flowtable_type(type: &flowtable_inet); |
106 | nft_unregister_flowtable_type(type: &flowtable_ipv6); |
107 | nft_unregister_flowtable_type(type: &flowtable_ipv4); |
108 | } |
109 | |
110 | module_init(nf_flow_inet_module_init); |
111 | module_exit(nf_flow_inet_module_exit); |
112 | |
113 | MODULE_LICENSE("GPL" ); |
114 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>" ); |
115 | MODULE_ALIAS_NF_FLOWTABLE(AF_INET); |
116 | MODULE_ALIAS_NF_FLOWTABLE(AF_INET6); |
117 | MODULE_ALIAS_NF_FLOWTABLE(1); /* NFPROTO_INET */ |
118 | MODULE_DESCRIPTION("Netfilter flow table mixed IPv4/IPv6 module" ); |
119 | |