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 | 23 | #include <linux/types.h> |
| 24 | 24 | #include <linux/ip.h> |
| 25 | 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) | |
| 30 | −#define XT_OSF_TTL (1<<1) | |
| 31 | −#define XT_OSF_LOG (1<<2) | |
| 32 | −#define XT_OSF_INVERT (1<<3) | |
| 31 | +#define XT_OSF_TTL NF_OSF_TTL | |
| 32 | +#define XT_OSF_LOG NF_OSF_LOG | |
| 33 | 33 | |
| 34 | −#define XT_OSF_LOGLEVEL_ALL 0 /* log all matched fingerprints */ | |
| 35 | −#define XT_OSF_LOGLEVEL_FIRST 1 /* log only the first matced fingerprint */ | |
| 36 | −#define XT_OSF_LOGLEVEL_ALL_KNOWN 2 /* do not log unknown packets */ | |
| 34 | +#define XT_OSF_LOGLEVEL_ALL NF_OSF_LOGLEVEL_ALL | |
| 35 | +#define XT_OSF_LOGLEVEL_FIRST NF_OSF_LOGLEVEL_FIRST | |
| 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 */ | |
| 39 | −#define XT_OSF_TTL_LESS 1 /* Check if ip TTL is less than fingerprint one */ | |
| 40 | −#define XT_OSF_TTL_NOCHECK 2 /* Do not compare ip and fingerprint TTL at all */ | |
| 38 | +#define XT_OSF_TTL_TRUE NF_OSF_TTL_TRUE | |
| 39 | +#define XT_OSF_TTL_NOCHECK NF_OSF_TTL_NOCHECK | |
| 41 | 40 | |
| 42 | −struct xt_osf_info { | |
| 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]; | |
| 41 | +#define XT_OSF_TTL_LESS 1 /* Check if ip TTL is less than fingerprint one */ | |
| 79 | 42 | |
| 80 | − /* MAX_IPOPTLEN is maximum if all options are NOPs or EOLs */ | |
| 81 | − struct xt_osf_opt opt[MAX_IPOPTLEN]; | |
| 82 | −}; | |
| 83 | − | |
| 84 | −struct xt_osf_nlmsg { | |
| 85 | − struct xt_osf_user_finger f; | |
| 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 | −}; | |
| 43 | +#define xt_osf_wc nf_osf_wc | |
| 44 | +#define xt_osf_opt nf_osf_opt | |
| 45 | +#define xt_osf_info nf_osf_info | |
| 46 | +#define xt_osf_user_finger nf_osf_user_finger | |
| 47 | +#define xt_osf_finger nf_osf_finger | |
| 48 | +#define xt_osf_nlmsg nf_osf_nlmsg | |
| 121 | 49 | |
| 122 | 50 | /* |
| 123 | 51 | * Add/remove fingerprint from the kernel. |
| @@ -444,6 +444,9 @@ config NETFILTER_SYNPROXY · 443 unmodified lines | ||
| 444 | 444 | |
| 445 | 445 | endif # NF_CONNTRACK |
| 446 | 446 | |
| 447 | +config NF_OSF | |
| 448 | + tristate 'Passive OS fingerprint infrastructure' | |
| 449 | + | |
| 447 | 450 | config NF_TABLES |
| 448 | 451 | select NETFILTER_NETLINK |
| 449 | 452 | tristate "Netfilter nf_tables support" |
| @@ -1358,6 +1361,7 @@ config NETFILTER_XT_MATCH_NFACCT · 908 unmodified lines | ||
| 1358 | 1361 | config NETFILTER_XT_MATCH_OSF |
| 1359 | 1362 | tristate '"osf" Passive OS fingerprint match' |
| 1360 | 1363 | depends on NETFILTER_ADVANCED && NETFILTER_NETLINK |
| 1364 | + select NF_OSF | |
| 1361 | 1365 | help |
| 1362 | 1366 | This option selects the Passive OS Fingerprinting match module |
| 1363 | 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 | 101 | obj-$(CONFIG_NFT_FIB) += nft_fib.o |
| 102 | 102 | obj-$(CONFIG_NFT_FIB_INET) += nft_fib_inet.o |
| 103 | 103 | obj-$(CONFIG_NFT_FIB_NETDEV) += nft_fib_netdev.o |
| 104 | +obj-$(CONFIG_NF_OSF) += nf_osf.o | |
| 104 | 105 | |
| 105 | 106 | # nf_tables netdev |
| 106 | 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 | 37 | #include <net/netfilter/nf_log.h> |
| 38 | 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 | 41 | * Indexed by dont-fragment bit. |
| 57 | 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 | 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 | 152 | static bool |
| 199 | 153 | xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p) |
| 200 | 154 | { |
| 201 | 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 | 156 | struct net *net = xt_net(p); |
| 214 | 157 | |
| 215 | 158 | if (!info) |
| 216 | 159 | return false; |
| 217 | 160 | |
| 218 | − tcp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(struct tcphdr), &_tcph); | |
| 219 | − if (!tcp) | |
| 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; | |
| 161 | + return nf_osf_match(skb, xt_family(p), xt_hooknum(p), xt_in(p), | |
| 162 | + xt_out(p), info, net, xt_osf_fingers); | |
| 361 | 163 | } |
| 362 | 164 | |
| 363 | 165 | static struct xt_match xt_osf_match = { |