Add nf_osf_ttl() and nf_osf_match() into nf_osf.c to prepare for nf_tables support.
Unified · Split
| @@ -0,0 +1,27 @@ | |||
| 1 | #include <uapi/linux/netfilter/nf_osf.h> | ||
| 2 | |||
| 3 | /* Initial window size option state machine: multiple of mss, mtu or | ||
| 4 | * plain numeric value. Can also be made as plain numeric value which | ||
| 5 | * is not a multiple of specified value. | ||
| 6 | */ | ||
| 7 | enum nf_osf_window_size_options { | ||
| 8 | OSF_WSS_PLAIN = 0, | ||
| 9 | OSF_WSS_MSS, | ||
| 10 | OSF_WSS_MTU, | ||
| 11 | OSF_WSS_MODULO, | ||
| 12 | OSF_WSS_MAX, | ||
| 13 | }; | ||
| 14 | |||
| 15 | enum osf_fmatch_states { | ||
| 16 | /* Packet does not match the fingerprint */ | ||
| 17 | FMATCH_WRONG = 0, | ||
| 18 | /* Packet matches the fingerprint */ | ||
| 19 | FMATCH_OK, | ||
| 20 | /* Options do not match the fingerprint, but header does */ | ||
| 21 | FMATCH_OPT_WRONG, | ||
| 22 | }; | ||
| 23 | |||
| 24 | bool nf_osf_match(const struct sk_buff *skb, u_int8_t family, | ||
| 25 | int hooknum, struct net_device *in, struct net_device *out, | ||
| 26 | const struct nf_osf_info *info, struct net *net, | ||
| 27 | const struct list_head *nf_osf_fingers); | ||
| @@ -0,0 +1,90 @@ | |||
| 1 | #ifndef _NF_OSF_H | ||
| 2 | #define _NF_OSF_H | ||
| 3 | |||
| 4 | #define MAXGENRELEN 32 | ||
| 5 | |||
| 6 | #define NF_OSF_GENRE (1 << 0) | ||
| 7 | #define NF_OSF_TTL (1 << 1) | ||
| 8 | #define NF_OSF_LOG (1 << 2) | ||
| 9 | #define NF_OSF_INVERT (1 << 3) | ||
| 10 | |||
| 11 | #define NF_OSF_LOGLEVEL_ALL 0 /* log all matched fingerprints */ | ||
| 12 | #define NF_OSF_LOGLEVEL_FIRST 1 /* log only the first matced fingerprint */ | ||
| 13 | #define NF_OSF_LOGLEVEL_ALL_KNOWN 2 /* do not log unknown packets */ | ||
| 14 | |||
| 15 | #define NF_OSF_TTL_TRUE 0 /* True ip and fingerprint TTL comparison */ | ||
| 16 | |||
| 17 | /* Do not compare ip and fingerprint TTL at all */ | ||
| 18 | #define NF_OSF_TTL_NOCHECK 2 | ||
| 19 | |||
| 20 | /* Wildcard MSS (kind of). | ||
| 21 | * It is used to implement a state machine for the different wildcard values | ||
| 22 | * of the MSS and window sizes. | ||
| 23 | */ | ||
| 24 | struct nf_osf_wc { | ||
| 25 | __u32 wc; | ||
| 26 | __u32 val; | ||
| 27 | }; | ||
| 28 | |||
| 29 | /* This struct represents IANA options | ||
| 30 | * http://www.iana.org/assignments/tcp-parameters | ||
| 31 | */ | ||
| 32 | struct nf_osf_opt { | ||
| 33 | __u16 kind, length; | ||
| 34 | struct nf_osf_wc wc; | ||
| 35 | }; | ||
| 36 | |||
| 37 | struct nf_osf_info { | ||
| 38 | char genre[MAXGENRELEN]; | ||
| 39 | __u32 len; | ||
| 40 | __u32 flags; | ||
| 41 | __u32 loglevel; | ||
| 42 | __u32 ttl; | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct nf_osf_user_finger { | ||
| 46 | struct nf_osf_wc wss; | ||
| 47 | |||
| 48 | __u8 ttl, df; | ||
| 49 | __u16 ss, mss; | ||
| 50 | __u16 opt_num; | ||
| 51 | |||
| 52 | char genre[MAXGENRELEN]; | ||
| 53 | char version[MAXGENRELEN]; | ||
| 54 | char subtype[MAXGENRELEN]; | ||
| 55 | |||
| 56 | /* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */ | ||
| 57 | struct nf_osf_opt opt[MAX_IPOPTLEN]; | ||
| 58 | }; | ||
| 59 | |||
| 60 | struct nf_osf_finger { | ||
| 61 | struct rcu_head rcu_head; | ||
| 62 | struct list_head finger_entry; | ||
| 63 | struct nf_osf_user_finger finger; | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct nf_osf_nlmsg { | ||
| 67 | struct nf_osf_user_finger f; | ||
| 68 | struct iphdr ip; | ||
| 69 | struct tcphdr tcp; | ||
| 70 | }; | ||
| 71 | |||
| 72 | /* Defines for IANA option kinds */ | ||
| 73 | enum iana_options { | ||
| 74 | OSFOPT_EOL = 0, /* End of options */ | ||
| 75 | OSFOPT_NOP, /* NOP */ | ||
| 76 | OSFOPT_MSS, /* Maximum segment size */ | ||
| 77 | OSFOPT_WSO, /* Window scale option */ | ||
| 78 | OSFOPT_SACKP, /* SACK permitted */ | ||
| 79 | OSFOPT_SACK, /* SACK */ | ||
| 80 | OSFOPT_ECHO, | ||
| 81 | OSFOPT_ECHOREPLY, | ||
| 82 | OSFOPT_TS, /* Timestamp option */ | ||
| 83 | OSFOPT_POCP, /* Partial Order Connection Permitted */ | ||
| 84 | OSFOPT_POSP, /* Partial Order Service Profile */ | ||
| 85 | |||
| 86 | /* Others are not used in the current OSF */ | ||
| 87 | OSFOPT_EMPTY = 255, | ||
| 88 | }; | ||
| 89 | |||
| 90 | #endif /* _NF_OSF_H */ | ||
| @@ -23,101 +23,29 @@ · 22 unmodified lines | |||
| 23 | #include <linux/types.h> | 23 | #include <linux/types.h> |
| 24 | #include <linux/ip.h> | 24 | #include <linux/ip.h> |
| 25 | #include <linux/tcp.h> | 25 | #include <linux/tcp.h> |
| 26 | #include <linux/netfilter/nf_osf.h> | ||
| 26 | 27 | ||
| 27 | #define MAXGENRELEN 32 | 28 | #define XT_OSF_GENRE NF_OSF_GENRE |
| 29 | #define XT_OSF_INVERT NF_OSF_INVERT | ||
| 28 | 30 | ||
| 29 | #define XT_OSF_GENRE (1<<0) | 31 | #define XT_OSF_TTL NF_OSF_TTL |
| 30 | #define XT_OSF_TTL (1<<1) | 32 | #define XT_OSF_LOG NF_OSF_LOG |
| 31 | #define XT_OSF_LOG (1<<2) | ||
| 32 | #define XT_OSF_INVERT (1<<3) | ||
| 33 | 33 | ||
| 34 | #define XT_OSF_LOGLEVEL_ALL 0 /* log all matched fingerprints */ | 34 | #define XT_OSF_LOGLEVEL_ALL NF_OSF_LOGLEVEL_ALL |
| 35 | #define XT_OSF_LOGLEVEL_FIRST 1 /* log only the first matced fingerprint */ | 35 | #define XT_OSF_LOGLEVEL_FIRST NF_OSF_LOGLEVEL_FIRST |
| 36 | #define XT_OSF_LOGLEVEL_ALL_KNOWN 2 /* do not log unknown packets */ | 36 | #define XT_OSF_LOGLEVEL_ALL_KNOWN NF_OSF_LOGLEVEL_ALL_KNOWN |
| 37 | 37 | ||
| 38 | #define XT_OSF_TTL_TRUE 0 /* True ip and fingerprint TTL comparison */ | 38 | #define XT_OSF_TTL_TRUE NF_OSF_TTL_TRUE |
| 39 | #define XT_OSF_TTL_LESS 1 /* Check if ip TTL is less than fingerprint one */ | 39 | #define XT_OSF_TTL_NOCHECK NF_OSF_TTL_NOCHECK |
| 40 | #define XT_OSF_TTL_NOCHECK 2 /* Do not compare ip and fingerprint TTL at all */ | ||
| 41 | 40 | ||
| 42 | struct xt_osf_info { | 41 | #define XT_OSF_TTL_LESS 1 /* Check if ip TTL is less than fingerprint one */ |
| 43 | char genre[MAXGENRELEN]; | ||
| 44 | __u32 len; | ||
| 45 | __u32 flags; | ||
| 46 | __u32 loglevel; | ||
| 47 | __u32 ttl; | ||
| 48 | }; | ||
| 49 | |||
| 50 | /* | ||
| 51 | * Wildcard MSS (kind of). | ||
| 52 | * It is used to implement a state machine for the different wildcard values | ||
| 53 | * of the MSS and window sizes. | ||
| 54 | */ | ||
| 55 | struct xt_osf_wc { | ||
| 56 | __u32 wc; | ||
| 57 | __u32 val; | ||
| 58 | }; | ||
| 59 | |||
| 60 | /* | ||
| 61 | * This struct represents IANA options | ||
| 62 | * http://www.iana.org/assignments/tcp-parameters | ||
| 63 | */ | ||
| 64 | struct xt_osf_opt { | ||
| 65 | __u16 kind, length; | ||
| 66 | struct xt_osf_wc wc; | ||
| 67 | }; | ||
| 68 | |||
| 69 | struct xt_osf_user_finger { | ||
| 70 | struct xt_osf_wc wss; | ||
| 71 | |||
| 72 | __u8 ttl, df; | ||
| 73 | __u16 ss, mss; | ||
| 74 | __u16 opt_num; | ||
| 75 | |||
| 76 | char genre[MAXGENRELEN]; | ||
| 77 | char version[MAXGENRELEN]; | ||
| 78 | char subtype[MAXGENRELEN]; | ||
| 79 | 42 | ||
| 80 | /* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */ | 43 | #define xt_osf_wc nf_osf_wc |
| 81 | struct xt_osf_opt opt[MAX_IPOPTLEN]; | 44 | #define xt_osf_opt nf_osf_opt |
| 82 | }; | 45 | #define xt_osf_info nf_osf_info |
| 83 | 46 | #define xt_osf_user_finger nf_osf_user_finger | |
| 84 | struct xt_osf_nlmsg { | 47 | #define xt_osf_finger nf_osf_finger |
| 85 | struct xt_osf_user_finger f; | 48 | #define xt_osf_nlmsg nf_osf_nlmsg |
| 86 | struct iphdr ip; | ||
| 87 | struct tcphdr tcp; | ||
| 88 | }; | ||
| 89 | |||
| 90 | /* Defines for IANA option kinds */ | ||
| 91 | |||
| 92 | enum iana_options { | ||
| 93 | OSFOPT_EOL = 0, /* End of options */ | ||
| 94 | OSFOPT_NOP, /* NOP */ | ||
| 95 | OSFOPT_MSS, /* Maximum segment size */ | ||
| 96 | OSFOPT_WSO, /* Window scale option */ | ||
| 97 | OSFOPT_SACKP, /* SACK permitted */ | ||
| 98 | OSFOPT_SACK, /* SACK */ | ||
| 99 | OSFOPT_ECHO, | ||
| 100 | OSFOPT_ECHOREPLY, | ||
| 101 | OSFOPT_TS, /* Timestamp option */ | ||
| 102 | OSFOPT_POCP, /* Partial Order Connection Permitted */ | ||
| 103 | OSFOPT_POSP, /* Partial Order Service Profile */ | ||
| 104 | |||
| 105 | /* Others are not used in the current OSF */ | ||
| 106 | OSFOPT_EMPTY = 255, | ||
| 107 | }; | ||
| 108 | |||
| 109 | /* | ||
| 110 | * Initial window size option state machine: multiple of mss, mtu or | ||
| 111 | * plain numeric value. Can also be made as plain numeric value which | ||
| 112 | * is not a multiple of specified value. | ||
| 113 | */ | ||
| 114 | enum xt_osf_window_size_options { | ||
| 115 | OSF_WSS_PLAIN = 0, | ||
| 116 | OSF_WSS_MSS, | ||
| 117 | OSF_WSS_MTU, | ||
| 118 | OSF_WSS_MODULO, | ||
| 119 | OSF_WSS_MAX, | ||
| 120 | }; | ||
| 121 | 49 | ||
| 122 | /* | 50 | /* |
| 123 | * Add/remove fingerprint from the kernel. | 51 | * Add/remove fingerprint from the kernel. |
| @@ -444,6 +444,9 @@ config NETFILTER_SYNPROXY · 443 unmodified lines | |||
| 444 | 444 | ||
| 445 | endif # NF_CONNTRACK | 445 | endif # NF_CONNTRACK |
| 446 | 446 | ||
| 447 | config NF_OSF | ||
| 448 | tristate 'Passive OS fingerprint infrastructure' | ||
| 449 | |||
| 447 | config NF_TABLES | 450 | config NF_TABLES |
| 448 | select NETFILTER_NETLINK | 451 | select NETFILTER_NETLINK |
| 449 | tristate "Netfilter nf_tables support" | 452 | tristate "Netfilter nf_tables support" |
| @@ -1358,6 +1361,7 @@ config NETFILTER_XT_MATCH_NFACCT · 908 unmodified lines | |||
| 1358 | config NETFILTER_XT_MATCH_OSF | 1361 | config NETFILTER_XT_MATCH_OSF |
| 1359 | tristate '"osf" Passive OS fingerprint match' | 1362 | tristate '"osf" Passive OS fingerprint match' |
| 1360 | depends on NETFILTER_ADVANCED && NETFILTER_NETLINK | 1363 | depends on NETFILTER_ADVANCED && NETFILTER_NETLINK |
| 1364 | select NF_OSF | ||
| 1361 | help | 1365 | help |
| 1362 | This option selects the Passive OS Fingerprinting match module | 1366 | This option selects the Passive OS Fingerprinting match module |
| 1363 | that allows to passively match the remote operating system by | 1367 | that allows to passively match the remote operating system by |
| @@ -101,6 +101,7 @@ obj-$(CONFIG_NFT_HASH) += nft_hash.o · 100 unmodified lines | |||
| 101 | obj-$(CONFIG_NFT_FIB) += nft_fib.o | 101 | obj-$(CONFIG_NFT_FIB) += nft_fib.o |
| 102 | obj-$(CONFIG_NFT_FIB_INET) += nft_fib_inet.o | 102 | obj-$(CONFIG_NFT_FIB_INET) += nft_fib_inet.o |
| 103 | obj-$(CONFIG_NFT_FIB_NETDEV) += nft_fib_netdev.o | 103 | obj-$(CONFIG_NFT_FIB_NETDEV) += nft_fib_netdev.o |
| 104 | obj-$(CONFIG_NF_OSF) += nf_osf.o | ||
| 104 | 105 | ||
| 105 | # nf_tables netdev | 106 | # nf_tables netdev |
| 106 | obj-$(CONFIG_NFT_DUP_NETDEV) += nft_dup_netdev.o | 107 | obj-$(CONFIG_NFT_DUP_NETDEV) += nft_dup_netdev.o |
| @@ -0,0 +1,218 @@ | |||
| 1 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 2 | #include <linux/module.h> | ||
| 3 | #include <linux/kernel.h> | ||
| 4 | |||
| 5 | #include <linux/capability.h> | ||
| 6 | #include <linux/if.h> | ||
| 7 | #include <linux/inetdevice.h> | ||
| 8 | #include <linux/ip.h> | ||
| 9 | #include <linux/list.h> | ||
| 10 | #include <linux/rculist.h> | ||
| 11 | #include <linux/skbuff.h> | ||
| 12 | #include <linux/slab.h> | ||
| 13 | #include <linux/tcp.h> | ||
| 14 | |||
| 15 | #include <net/ip.h> | ||
| 16 | #include <net/tcp.h> | ||
| 17 | |||
| 18 | #include <linux/netfilter/nfnetlink.h> | ||
| 19 | #include <linux/netfilter/x_tables.h> | ||
| 20 | #include <net/netfilter/nf_log.h> | ||
| 21 | #include <linux/netfilter/nf_osf.h> | ||
| 22 | |||
| 23 | static inline int nf_osf_ttl(const struct sk_buff *skb, | ||
| 24 | const struct nf_osf_info *info, | ||
| 25 | unsigned char f_ttl) | ||
| 26 | { | ||
| 27 | const struct iphdr *ip = ip_hdr(skb); | ||
| 28 | |||
| 29 | if (info->flags & NF_OSF_TTL) { | ||
| 30 | if (info->ttl == NF_OSF_TTL_TRUE) | ||
| 31 | return ip->ttl == f_ttl; | ||
| 32 | if (info->ttl == NF_OSF_TTL_NOCHECK) | ||
| 33 | return 1; | ||
| 34 | else if (ip->ttl <= f_ttl) | ||
| 35 | return 1; | ||
| 36 | else { | ||
| 37 | struct in_device *in_dev = __in_dev_get_rcu(skb->dev); | ||
| 38 | int ret = 0; | ||
| 39 | |||
| 40 | for_ifa(in_dev) { | ||
| 41 | if (inet_ifa_match(ip->saddr, ifa)) { | ||
| 42 | ret = (ip->ttl == f_ttl); | ||
| 43 | break; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | endfor_ifa(in_dev); | ||
| 47 | |||
| 48 | return ret; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | return ip->ttl == f_ttl; | ||
| 53 | } | ||
| 54 | |||
| 55 | bool | ||
| 56 | nf_osf_match(const struct sk_buff *skb, u_int8_t family, | ||
| 57 | int hooknum, struct net_device *in, struct net_device *out, | ||
| 58 | const struct nf_osf_info *info, struct net *net, | ||
| 59 | const struct list_head *nf_osf_fingers) | ||
| 60 | { | ||
| 61 | const unsigned char *optp = NULL, *_optp = NULL; | ||
| 62 | unsigned int optsize = 0, check_WSS = 0; | ||
| 63 | int fmatch = FMATCH_WRONG, fcount = 0; | ||
| 64 | const struct iphdr *ip = ip_hdr(skb); | ||
| 65 | const struct nf_osf_user_finger *f; | ||
| 66 | unsigned char opts[MAX_IPOPTLEN]; | ||
| 67 | const struct nf_osf_finger *kf; | ||
| 68 | u16 window, totlen, mss = 0; | ||
| 69 | const struct tcphdr *tcp; | ||
| 70 | struct tcphdr _tcph; | ||
| 71 | bool df; | ||
| 72 | |||
| 73 | tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph); | ||
| 74 | if (!tcp) | ||
| 75 | return false; | ||
| 76 | |||
| 77 | if (!tcp->syn) | ||
| 78 | return false; | ||
| 79 | |||
| 80 | totlen = ntohs(ip->tot_len); | ||
| 81 | df = ntohs(ip->frag_off) & IP_DF; | ||
| 82 | window = ntohs(tcp->window); | ||
| 83 | |||
| 84 | if (tcp->doff * 4 > sizeof(struct tcphdr)) { | ||
| 85 | optsize = tcp->doff * 4 - sizeof(struct tcphdr); | ||
| 86 | |||
| 87 | _optp = optp = skb_header_pointer(skb, ip_hdrlen(skb) + | ||
| 88 | sizeof(struct tcphdr), optsize, opts); | ||
| 89 | } | ||
| 90 | |||
| 91 | list_for_each_entry_rcu(kf, &nf_osf_fingers[df], finger_entry) { | ||
| 92 | int foptsize, optnum; | ||
| 93 | |||
| 94 | f = &kf->finger; | ||
| 95 | |||
| 96 | if (!(info->flags & NF_OSF_LOG) && strcmp(info->genre, f->genre)) | ||
| 97 | continue; | ||
| 98 | |||
| 99 | optp = _optp; | ||
| 100 | fmatch = FMATCH_WRONG; | ||
| 101 | |||
| 102 | if (totlen != f->ss || !nf_osf_ttl(skb, info, f->ttl)) | ||
| 103 | continue; | ||
| 104 | |||
| 105 | /* | ||
| 106 | * Should not happen if userspace parser was written correctly. | ||
| 107 | */ | ||
| 108 | if (f->wss.wc >= OSF_WSS_MAX) | ||
| 109 | continue; | ||
| 110 | |||
| 111 | /* Check options */ | ||
| 112 | |||
| 113 | foptsize = 0; | ||
| 114 | for (optnum = 0; optnum < f->opt_num; ++optnum) | ||
| 115 | foptsize += f->opt[optnum].length; | ||
| 116 | |||
| 117 | if (foptsize > MAX_IPOPTLEN || | ||
| 118 | optsize > MAX_IPOPTLEN || | ||
| 119 | optsize != foptsize) | ||
| 120 | continue; | ||
| 121 | |||
| 122 | check_WSS = f->wss.wc; | ||
| 123 | |||
| 124 | for (optnum = 0; optnum < f->opt_num; ++optnum) { | ||
| 125 | if (f->opt[optnum].kind == (*optp)) { | ||
| 126 | __u32 len = f->opt[optnum].length; | ||
| 127 | const __u8 *optend = optp + len; | ||
| 128 | |||
| 129 | fmatch = FMATCH_OK; | ||
| 130 | |||
| 131 | switch (*optp) { | ||
| 132 | case OSFOPT_MSS: | ||
| 133 | mss = optp[3]; | ||
| 134 | mss <<= 8; | ||
| 135 | mss |= optp[2]; | ||
| 136 | |||
| 137 | mss = ntohs((__force __be16)mss); | ||
| 138 | break; | ||
| 139 | case OSFOPT_TS: | ||
| 140 | break; | ||
| 141 | } | ||
| 142 | |||
| 143 | optp = optend; | ||
| 144 | } else | ||
| 145 | fmatch = FMATCH_OPT_WRONG; | ||
| 146 | |||
| 147 | if (fmatch != FMATCH_OK) | ||
| 148 | break; | ||
| 149 | } | ||
| 150 | |||
| 151 | if (fmatch != FMATCH_OPT_WRONG) { | ||
| 152 | fmatch = FMATCH_WRONG; | ||
| 153 | |||
| 154 | switch (check_WSS) { | ||
| 155 | case OSF_WSS_PLAIN: | ||
| 156 | if (f->wss.val == 0 || window == f->wss.val) | ||
| 157 | fmatch = FMATCH_OK; | ||
| 158 | break; | ||
| 159 | case OSF_WSS_MSS: | ||
| 160 | /* | ||
| 161 | * Some smart modems decrease mangle MSS to | ||
| 162 | * SMART_MSS_2, so we check standard, decreased | ||
| 163 | * and the one provided in the fingerprint MSS | ||
| 164 | * values. | ||
| 165 | */ | ||
| 166 | #define SMART_MSS_1 1460 | ||
| 167 | #define SMART_MSS_2 1448 | ||
| 168 | if (window == f->wss.val * mss || | ||
| 169 | window == f->wss.val * SMART_MSS_1 || | ||
| 170 | window == f->wss.val * SMART_MSS_2) | ||
| 171 | fmatch = FMATCH_OK; | ||
| 172 | break; | ||
| 173 | case OSF_WSS_MTU: | ||
| 174 | if (window == f->wss.val * (mss + 40) || | ||
| 175 | window == f->wss.val * (SMART_MSS_1 + 40) || | ||
| 176 | window == f->wss.val * (SMART_MSS_2 + 40)) | ||
| 177 | fmatch = FMATCH_OK; | ||
| 178 | break; | ||
| 179 | case OSF_WSS_MODULO: | ||
| 180 | if ((window % f->wss.val) == 0) | ||
| 181 | fmatch = FMATCH_OK; | ||
| 182 | break; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | if (fmatch != FMATCH_OK) | ||
| 187 | continue; | ||
| 188 | |||
| 189 | fcount++; | ||
| 190 | |||
| 191 | if (info->flags & NF_OSF_LOG) | ||
| 192 | nf_log_packet(net, family, hooknum, skb, | ||
| 193 | in, out, NULL, | ||
| 194 | "%s [%s:%s] : %pI4:%d -> %pI4:%d hops=%d\n", | ||
| 195 | f->genre, f->version, f->subtype, | ||
| 196 | &ip->saddr, ntohs(tcp->source), | ||
| 197 | &ip->daddr, ntohs(tcp->dest), | ||
| 198 | f->ttl - ip->ttl); | ||
| 199 | |||
| 200 | if ((info->flags & NF_OSF_LOG) && | ||
| 201 | info->loglevel == NF_OSF_LOGLEVEL_FIRST) | ||
| 202 | break; | ||
| 203 | } | ||
| 204 | |||
| 205 | if (!fcount && (info->flags & NF_OSF_LOG)) | ||
| 206 | nf_log_packet(net, family, hooknum, skb, in, out, NULL, | ||
| 207 | "Remote OS is not known: %pI4:%u -> %pI4:%u\n", | ||
| 208 | &ip->saddr, ntohs(tcp->source), | ||
| 209 | &ip->daddr, ntohs(tcp->dest)); | ||
| 210 | |||
| 211 | if (fcount) | ||
| 212 | fmatch = FMATCH_OK; | ||
| 213 | |||
| 214 | return fmatch == FMATCH_OK; | ||
| 215 | } | ||
| 216 | EXPORT_SYMBOL_GPL(nf_osf_match); | ||
| 217 | |||
| 218 | MODULE_LICENSE("GPL"); | ||
| @@ -37,21 +37,6 @@ · 36 unmodified lines | |||
| 37 | #include <net/netfilter/nf_log.h> | 37 | #include <net/netfilter/nf_log.h> |
| 38 | #include <linux/netfilter/xt_osf.h> | 38 | #include <linux/netfilter/xt_osf.h> |
| 39 | 39 | ||
| 40 | struct xt_osf_finger { | ||
| 41 | struct rcu_head rcu_head; | ||
| 42 | struct list_head finger_entry; | ||
| 43 | struct xt_osf_user_finger finger; | ||
| 44 | }; | ||
| 45 | |||
| 46 | enum osf_fmatch_states { | ||
| 47 | /* Packet does not match the fingerprint */ | ||
| 48 | FMATCH_WRONG = 0, | ||
| 49 | /* Packet matches the fingerprint */ | ||
| 50 | FMATCH_OK, | ||
| 51 | /* Options do not match the fingerprint, but header does */ | ||
| 52 | FMATCH_OPT_WRONG, | ||
| 53 | }; | ||
| 54 | |||
| 55 | /* | 40 | /* |
| 56 | * Indexed by dont-fragment bit. | 41 | * Indexed by dont-fragment bit. |
| 57 | * It is the only constant value in the fingerprint. | 42 | * It is the only constant value in the fingerprint. |
| @@ -164,200 +149,17 @@ static const struct nfnetlink_subsystem xt_osf_nfnetlink = { · 106 unmodified lines | |||
| 164 | .cb = xt_osf_nfnetlink_callbacks, | 149 | .cb = xt_osf_nfnetlink_callbacks, |
| 165 | }; | 150 | }; |
| 166 | 151 | ||
| 167 | static inline int xt_osf_ttl(const struct sk_buff *skb, const struct xt_osf_info *info, | ||
| 168 | unsigned char f_ttl) | ||
| 169 | { | ||
| 170 | const struct iphdr *ip = ip_hdr(skb); | ||
| 171 | |||
| 172 | if (info->flags & XT_OSF_TTL) { | ||
| 173 | if (info->ttl == XT_OSF_TTL_TRUE) | ||
| 174 | return ip->ttl == f_ttl; | ||
| 175 | if (info->ttl == XT_OSF_TTL_NOCHECK) | ||
| 176 | return 1; | ||
| 177 | else if (ip->ttl <= f_ttl) | ||
| 178 | return 1; | ||
| 179 | else { | ||
| 180 | struct in_device *in_dev = __in_dev_get_rcu(skb->dev); | ||
| 181 | int ret = 0; | ||
| 182 | |||
| 183 | for_ifa(in_dev) { | ||
| 184 | if (inet_ifa_match(ip->saddr, ifa)) { | ||
| 185 | ret = (ip->ttl == f_ttl); | ||
| 186 | break; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | endfor_ifa(in_dev); | ||
| 190 | |||
| 191 | return ret; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | return ip->ttl == f_ttl; | ||
| 196 | } | ||
| 197 | |||
| 198 | static bool | 152 | static bool |
| 199 | xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p) | 153 | xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p) |
| 200 | { | 154 | { |
| 201 | const struct xt_osf_info *info = p->matchinfo; | 155 | const struct xt_osf_info *info = p->matchinfo; |
| 202 | const struct iphdr *ip = ip_hdr(skb); | ||
| 203 | const struct tcphdr *tcp; | ||
| 204 | struct tcphdr _tcph; | ||
| 205 | int fmatch = FMATCH_WRONG, fcount = 0; | ||
| 206 | unsigned int optsize = 0, check_WSS = 0; | ||
| 207 | u16 window, totlen, mss = 0; | ||
| 208 | bool df; | ||
| 209 | const unsigned char *optp = NULL, *_optp = NULL; | ||
| 210 | unsigned char opts[MAX_IPOPTLEN]; | ||
| 211 | const struct xt_osf_finger *kf; | ||
| 212 | const struct xt_osf_user_finger *f; | ||
| 213 | struct net *net = xt_net(p); | 156 | struct net *net = xt_net(p); |
| 214 | 157 | ||
| 215 | if (!info) | 158 | if (!info) |
| 216 | return false; | 159 | return false; |
| 217 | 160 | ||
| 218 | tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph); | 161 | return nf_osf_match(skb, xt_family(p), xt_hooknum(p), xt_in(p), |
| 219 | if (!tcp) | 162 | xt_out(p), info, net, xt_osf_fingers); |
| 220 | return false; | ||
| 221 | |||
| 222 | if (!tcp->syn) | ||
| 223 | return false; | ||
| 224 | |||
| 225 | totlen = ntohs(ip->tot_len); | ||
| 226 | df = ntohs(ip->frag_off) & IP_DF; | ||
| 227 | window = ntohs(tcp->window); | ||
| 228 | |||
| 229 | if (tcp->doff * 4 > sizeof(struct tcphdr)) { | ||
| 230 | optsize = tcp->doff * 4 - sizeof(struct tcphdr); | ||
| 231 | |||
| 232 | _optp = optp = skb_header_pointer(skb, ip_hdrlen(skb) + | ||
| 233 | sizeof(struct tcphdr), optsize, opts); | ||
| 234 | } | ||
| 235 | |||
| 236 | list_for_each_entry_rcu(kf, &xt_osf_fingers[df], finger_entry) { | ||
| 237 | int foptsize, optnum; | ||
| 238 | |||
| 239 | f = &kf->finger; | ||
| 240 | |||
| 241 | if (!(info->flags & XT_OSF_LOG) && strcmp(info->genre, f->genre)) | ||
| 242 | continue; | ||
| 243 | |||
| 244 | optp = _optp; | ||
| 245 | fmatch = FMATCH_WRONG; | ||
| 246 | |||
| 247 | if (totlen != f->ss || !xt_osf_ttl(skb, info, f->ttl)) | ||
| 248 | continue; | ||
| 249 | |||
| 250 | /* | ||
| 251 | * Should not happen if userspace parser was written correctly. | ||
| 252 | */ | ||
| 253 | if (f->wss.wc >= OSF_WSS_MAX) | ||
| 254 | continue; | ||
| 255 | |||
| 256 | /* Check options */ | ||
| 257 | |||
| 258 | foptsize = 0; | ||
| 259 | for (optnum = 0; optnum < f->opt_num; ++optnum) | ||
| 260 | foptsize += f->opt[optnum].length; | ||
| 261 | |||
| 262 | if (foptsize > MAX_IPOPTLEN || | ||
| 263 | optsize > MAX_IPOPTLEN || | ||
| 264 | optsize != foptsize) | ||
| 265 | continue; | ||
| 266 | |||
| 267 | check_WSS = f->wss.wc; | ||
| 268 | |||
| 269 | for (optnum = 0; optnum < f->opt_num; ++optnum) { | ||
| 270 | if (f->opt[optnum].kind == (*optp)) { | ||
| 271 | __u32 len = f->opt[optnum].length; | ||
| 272 | const __u8 *optend = optp + len; | ||
| 273 | |||
| 274 | fmatch = FMATCH_OK; | ||
| 275 | |||
| 276 | switch (*optp) { | ||
| 277 | case OSFOPT_MSS: | ||
| 278 | mss = optp[3]; | ||
| 279 | mss <<= 8; | ||
| 280 | mss |= optp[2]; | ||
| 281 | |||
| 282 | mss = ntohs((__force __be16)mss); | ||
| 283 | break; | ||
| 284 | case OSFOPT_TS: | ||
| 285 | break; | ||
| 286 | } | ||
| 287 | |||
| 288 | optp = optend; | ||
| 289 | } else | ||
| 290 | fmatch = FMATCH_OPT_WRONG; | ||
| 291 | |||
| 292 | if (fmatch != FMATCH_OK) | ||
| 293 | break; | ||
| 294 | } | ||
| 295 | |||
| 296 | if (fmatch != FMATCH_OPT_WRONG) { | ||
| 297 | fmatch = FMATCH_WRONG; | ||
| 298 | |||
| 299 | switch (check_WSS) { | ||
| 300 | case OSF_WSS_PLAIN: | ||
| 301 | if (f->wss.val == 0 || window == f->wss.val) | ||
| 302 | fmatch = FMATCH_OK; | ||
| 303 | break; | ||
| 304 | case OSF_WSS_MSS: | ||
| 305 | /* | ||
| 306 | * Some smart modems decrease mangle MSS to | ||
| 307 | * SMART_MSS_2, so we check standard, decreased | ||
| 308 | * and the one provided in the fingerprint MSS | ||
| 309 | * values. | ||
| 310 | */ | ||
| 311 | #define SMART_MSS_1 1460 | ||
| 312 | #define SMART_MSS_2 1448 | ||
| 313 | if (window == f->wss.val * mss || | ||
| 314 | window == f->wss.val * SMART_MSS_1 || | ||
| 315 | window == f->wss.val * SMART_MSS_2) | ||
| 316 | fmatch = FMATCH_OK; | ||
| 317 | break; | ||
| 318 | case OSF_WSS_MTU: | ||
| 319 | if (window == f->wss.val * (mss + 40) || | ||
| 320 | window == f->wss.val * (SMART_MSS_1 + 40) || | ||
| 321 | window == f->wss.val * (SMART_MSS_2 + 40)) | ||
| 322 | fmatch = FMATCH_OK; | ||
| 323 | break; | ||
| 324 | case OSF_WSS_MODULO: | ||
| 325 | if ((window % f->wss.val) == 0) | ||
| 326 | fmatch = FMATCH_OK; | ||
| 327 | break; | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | if (fmatch != FMATCH_OK) | ||
| 332 | continue; | ||
| 333 | |||
| 334 | fcount++; | ||
| 335 | |||
| 336 | if (info->flags & XT_OSF_LOG) | ||
| 337 | nf_log_packet(net, xt_family(p), xt_hooknum(p), skb, | ||
| 338 | xt_in(p), xt_out(p), NULL, | ||
| 339 | "%s [%s:%s] : %pI4:%d -> %pI4:%d hops=%d\n", | ||
| 340 | f->genre, f->version, f->subtype, | ||
| 341 | &ip->saddr, ntohs(tcp->source), | ||
| 342 | &ip->daddr, ntohs(tcp->dest), | ||
| 343 | f->ttl - ip->ttl); | ||
| 344 | |||
| 345 | if ((info->flags & XT_OSF_LOG) && | ||
| 346 | info->loglevel == XT_OSF_LOGLEVEL_FIRST) | ||
| 347 | break; | ||
| 348 | } | ||
| 349 | |||
| 350 | if (!fcount && (info->flags & XT_OSF_LOG)) | ||
| 351 | nf_log_packet(net, xt_family(p), xt_hooknum(p), skb, xt_in(p), | ||
| 352 | xt_out(p), NULL, | ||
| 353 | "Remote OS is not known: %pI4:%u -> %pI4:%u\n", | ||
| 354 | &ip->saddr, ntohs(tcp->source), | ||
| 355 | &ip->daddr, ntohs(tcp->dest)); | ||
| 356 | |||
| 357 | if (fcount) | ||
| 358 | fmatch = FMATCH_OK; | ||
| 359 | |||
| 360 | return fmatch == FMATCH_OK; | ||
| 361 | } | 163 | } |
| 362 | 164 | ||
| 363 | static struct xt_match xt_osf_match = { | 165 | static struct xt_match xt_osf_match = { |