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 | if (!nf_flow_pppoe_proto(skb, inner_proto: &proto)) |
25 | return NF_ACCEPT; |
26 | break; |
27 | default: |
28 | proto = skb->protocol; |
29 | break; |
30 | } |
31 | |
32 | switch (proto) { |
33 | case htons(ETH_P_IP): |
34 | return nf_flow_offload_ip_hook(priv, skb, state); |
35 | case htons(ETH_P_IPV6): |
36 | return nf_flow_offload_ipv6_hook(priv, skb, state); |
37 | } |
38 | |
39 | return NF_ACCEPT; |
40 | } |
41 | |
42 | static int nf_flow_rule_route_inet(struct net *net, |
43 | struct flow_offload *flow, |
44 | enum flow_offload_tuple_dir dir, |
45 | struct nf_flow_rule *flow_rule) |
46 | { |
47 | const struct flow_offload_tuple *flow_tuple = &flow->tuplehash[dir].tuple; |
48 | int err; |
49 | |
50 | switch (flow_tuple->l3proto) { |
51 | case NFPROTO_IPV4: |
52 | err = nf_flow_rule_route_ipv4(net, flow, dir, flow_rule); |
53 | break; |
54 | case NFPROTO_IPV6: |
55 | err = nf_flow_rule_route_ipv6(net, flow, dir, flow_rule); |
56 | break; |
57 | default: |
58 | err = -1; |
59 | break; |
60 | } |
61 | |
62 | return err; |
63 | } |
64 | |
65 | static struct nf_flowtable_type flowtable_inet = { |
66 | .family = NFPROTO_INET, |
67 | .init = nf_flow_table_init, |
68 | .setup = nf_flow_table_offload_setup, |
69 | .action = nf_flow_rule_route_inet, |
70 | .free = nf_flow_table_free, |
71 | .hook = nf_flow_offload_inet_hook, |
72 | .owner = THIS_MODULE, |
73 | }; |
74 | |
75 | static struct nf_flowtable_type flowtable_ipv4 = { |
76 | .family = NFPROTO_IPV4, |
77 | .init = nf_flow_table_init, |
78 | .setup = nf_flow_table_offload_setup, |
79 | .action = nf_flow_rule_route_ipv4, |
80 | .free = nf_flow_table_free, |
81 | .hook = nf_flow_offload_ip_hook, |
82 | .owner = THIS_MODULE, |
83 | }; |
84 | |
85 | static struct nf_flowtable_type flowtable_ipv6 = { |
86 | .family = NFPROTO_IPV6, |
87 | .init = nf_flow_table_init, |
88 | .setup = nf_flow_table_offload_setup, |
89 | .action = nf_flow_rule_route_ipv6, |
90 | .free = nf_flow_table_free, |
91 | .hook = nf_flow_offload_ipv6_hook, |
92 | .owner = THIS_MODULE, |
93 | }; |
94 | |
95 | static int __init nf_flow_inet_module_init(void) |
96 | { |
97 | nft_register_flowtable_type(type: &flowtable_ipv4); |
98 | nft_register_flowtable_type(type: &flowtable_ipv6); |
99 | nft_register_flowtable_type(type: &flowtable_inet); |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | static void __exit nf_flow_inet_module_exit(void) |
105 | { |
106 | nft_unregister_flowtable_type(type: &flowtable_inet); |
107 | nft_unregister_flowtable_type(type: &flowtable_ipv6); |
108 | nft_unregister_flowtable_type(type: &flowtable_ipv4); |
109 | } |
110 | |
111 | module_init(nf_flow_inet_module_init); |
112 | module_exit(nf_flow_inet_module_exit); |
113 | |
114 | MODULE_LICENSE("GPL" ); |
115 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>" ); |
116 | MODULE_ALIAS_NF_FLOWTABLE(AF_INET); |
117 | MODULE_ALIAS_NF_FLOWTABLE(AF_INET6); |
118 | MODULE_ALIAS_NF_FLOWTABLE(1); /* NFPROTO_INET */ |
119 | MODULE_DESCRIPTION("Netfilter flow table mixed IPv4/IPv6 module" ); |
120 | |