2 * Copyright (C) 2017 jianhui zhao <zhaojh329@gmail.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
9 #include <linux/init.h>
10 #include <linux/module.h>
11 #include <linux/version.h>
14 #include <linux/tcp.h>
15 #include <linux/udp.h>
16 #include <net/netfilter/nf_nat.h>
17 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)
18 #include <net/netfilter/nf_nat_l3proto.h>
24 #define IPS_HIJACKED (1 << 31)
25 #define IPS_ALLOWED (1 << 30)
27 #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 17, 19)
28 static u32
wd_nat_setup_info(struct sk_buff
*skb
, struct nf_conn
*ct
)
30 static u32
wd_nat_setup_info(void *priv
, struct sk_buff
*skb
,
31 const struct nf_hook_state
*state
, struct nf_conn
*ct
)
34 struct config
*conf
= get_config();
35 struct tcphdr
*tcph
= tcp_hdr(skb
);
36 union nf_conntrack_man_proto proto
;
37 #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 17, 19)
38 struct nf_nat_range2 newrange
= {};
40 struct nf_nat_range newrange
= {};
42 static uint16_t PORT_80
= htons(80);
44 proto
.tcp
.port
= (tcph
->dest
== PORT_80
) ? htons(conf
->port
) : htons(conf
->ssl_port
);
45 newrange
.flags
= NF_NAT_RANGE_MAP_IPS
| NF_NAT_RANGE_PROTO_SPECIFIED
;
46 newrange
.min_addr
.ip
= conf
->interface_ipaddr
;
47 newrange
.max_addr
.ip
= conf
->interface_ipaddr
;
48 newrange
.min_proto
= proto
;
49 newrange
.max_proto
= proto
;
51 ct
->status
|= IPS_HIJACKED
;
53 return nf_nat_setup_info(ct
, &newrange
, NF_NAT_MANIP_DST
);
56 static u32
wifidog_hook(void *priv
, struct sk_buff
*skb
, const struct nf_hook_state
*state
)
58 struct config
*conf
= get_config();
59 struct iphdr
*iph
= ip_hdr(skb
);
63 enum ip_conntrack_info ctinfo
;
64 static uint16_t PORT_80
= htons(80); /* http */
65 static uint16_t PORT_443
= htons(443); /* https */
66 static uint16_t PORT_67
= htons(67); /* dhcp */
67 static uint16_t PORT_53
= htons(53); /* dns */
69 if (unlikely(!conf
->enabled
))
72 if (state
->in
->ifindex
!= conf
->interface_ifindex
)
75 /* Accept broadcast */
76 if (skb
->pkt_type
== PACKET_BROADCAST
|| skb
->pkt_type
== PACKET_MULTICAST
)
79 /* Accept all to local area networks */
80 if ((iph
->daddr
| ~conf
->interface_mask
) == conf
->interface_broadcast
)
83 ct
= nf_ct_get(skb
, &ctinfo
);
84 if (!ct
|| (ct
->status
& IPS_ALLOWED
))
87 if (ct
->status
& IPS_HIJACKED
) {
88 if (is_allowed_mac(skb
, state
)) {
89 /* Avoid duplication of authentication */
90 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)
98 } else if (ctinfo
== IP_CT_NEW
&& (is_allowed_dest_ip(skb
, state
) || is_allowed_mac(skb
, state
))) {
99 ct
->status
|= IPS_ALLOWED
;
103 switch (iph
->protocol
) {
106 if(tcph
->dest
== PORT_53
|| tcph
->dest
== PORT_67
) {
107 ct
->status
|= IPS_ALLOWED
;
111 if (tcph
->dest
== PORT_80
|| tcph
->dest
== PORT_443
)
118 if(udph
->dest
== PORT_53
|| udph
->dest
== PORT_67
) {
119 ct
->status
|= IPS_ALLOWED
;
125 ct
->status
|= IPS_ALLOWED
;
130 /* all packets from unknown client are dropped */
131 if (ctinfo
!= IP_CT_NEW
|| (ct
->status
& IPS_DST_NAT_DONE
)) {
132 pr_debug("dropping packets of suspect stream, src:%pI4, dst:%pI4\n", &iph
->saddr
, &iph
->daddr
);
136 #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 17, 19)
137 return wd_nat_setup_info(skb
, ct
);
139 return nf_nat_ipv4_in(priv
, skb
, state
, wd_nat_setup_info
);
143 static struct nf_hook_ops wifidog_ops __read_mostly
= {
144 .hook
= wifidog_hook
,
146 .hooknum
= NF_INET_PRE_ROUTING
,
147 .priority
= NF_IP_PRI_NAT_DST
150 static int __init
wifidog_init(void)
158 #if LINUX_VERSION_CODE > KERNEL_VERSION(5, 0, 21)
159 ret
= nf_nat_ipv4_register_fn(&init_net
, &wifidog_ops
);
160 #elif LINUX_VERSION_CODE > KERNEL_VERSION(4, 17, 19)
161 ret
= nf_nat_l3proto_ipv4_register_fn(&init_net
, &wifidog_ops
);
162 #elif LINUX_VERSION_CODE > KERNEL_VERSION(4, 12, 14)
163 ret
= nf_register_net_hook(&init_net
, &wifidog_ops
);
165 ret
= nf_register_hook(&wifidog_ops
);
168 pr_err("can't register hook\n");
172 pr_info("kmod of wifidog is started\n");
181 static void __exit
wifidog_exit(void)
185 #if LINUX_VERSION_CODE > KERNEL_VERSION(5, 0, 21)
186 nf_nat_ipv4_unregister_fn(&init_net
, &wifidog_ops
);
187 #elif LINUX_VERSION_CODE > KERNEL_VERSION(4, 17, 19)
188 nf_nat_l3proto_ipv4_unregister_fn(&init_net
, &wifidog_ops
);
189 #elif LINUX_VERSION_CODE > KERNEL_VERSION(4, 12, 14)
190 nf_unregister_net_hook(&init_net
, &wifidog_ops
);
192 nf_unregister_hook(&wifidog_ops
);
195 pr_info("kmod of wifidog-ng is stop\n");
198 module_init(wifidog_init
);
199 module_exit(wifidog_exit
);
201 MODULE_AUTHOR("jianhui zhao <zhaojh329@gmail.com>");
202 MODULE_LICENSE("GPL");