whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

nf_conntrack_proto_gre.c (9629B)


      1 /*
      2  * ip_conntrack_proto_gre.c - Version 3.0
      3  *
      4  * Connection tracking protocol helper module for GRE.
      5  *
      6  * GRE is a generic encapsulation protocol, which is generally not very
      7  * suited for NAT, as it has no protocol-specific part as port numbers.
      8  *
      9  * It has an optional key field, which may help us distinguishing two
     10  * connections between the same two hosts.
     11  *
     12  * GRE is defined in RFC 1701 and RFC 1702, as well as RFC 2784
     13  *
     14  * PPTP is built on top of a modified version of GRE, and has a mandatory
     15  * field called "CallID", which serves us for the same purpose as the key
     16  * field in plain GRE.
     17  *
     18  * Documentation about PPTP can be found in RFC 2637
     19  *
     20  * (C) 2000-2005 by Harald Welte <laforge@gnumonks.org>
     21  *
     22  * Development of this code funded by Astaro AG (http://www.astaro.com/)
     23  *
     24  * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
     25  */
     26 
     27 #include <linux/module.h>
     28 #include <linux/types.h>
     29 #include <linux/timer.h>
     30 #include <linux/list.h>
     31 #include <linux/seq_file.h>
     32 #include <linux/in.h>
     33 #include <linux/netdevice.h>
     34 #include <linux/skbuff.h>
     35 #include <linux/slab.h>
     36 #include <net/dst.h>
     37 #include <net/net_namespace.h>
     38 #include <net/netns/generic.h>
     39 #include <net/netfilter/nf_conntrack_l4proto.h>
     40 #include <net/netfilter/nf_conntrack_helper.h>
     41 #include <net/netfilter/nf_conntrack_core.h>
     42 #include <net/netfilter/nf_conntrack_timeout.h>
     43 #include <linux/netfilter/nf_conntrack_proto_gre.h>
     44 #include <linux/netfilter/nf_conntrack_pptp.h>
     45 
     46 static const unsigned int gre_timeouts[GRE_CT_MAX] = {
     47 	[GRE_CT_UNREPLIED]	= 30*HZ,
     48 	[GRE_CT_REPLIED]	= 180*HZ,
     49 };
     50 
     51 /* used when expectation is added */
     52 static DEFINE_SPINLOCK(keymap_lock);
     53 
     54 static inline struct nf_gre_net *gre_pernet(struct net *net)
     55 {
     56 	return &net->ct.nf_ct_proto.gre;
     57 }
     58 
     59 void nf_ct_gre_keymap_flush(struct net *net)
     60 {
     61 	struct nf_gre_net *net_gre = gre_pernet(net);
     62 	struct nf_ct_gre_keymap *km, *tmp;
     63 
     64 	spin_lock_bh(&keymap_lock);
     65 	list_for_each_entry_safe(km, tmp, &net_gre->keymap_list, list) {
     66 		list_del_rcu(&km->list);
     67 		kfree_rcu(km, rcu);
     68 	}
     69 	spin_unlock_bh(&keymap_lock);
     70 }
     71 
     72 static inline int gre_key_cmpfn(const struct nf_ct_gre_keymap *km,
     73 				const struct nf_conntrack_tuple *t)
     74 {
     75 	return km->tuple.src.l3num == t->src.l3num &&
     76 	       !memcmp(&km->tuple.src.u3, &t->src.u3, sizeof(t->src.u3)) &&
     77 	       !memcmp(&km->tuple.dst.u3, &t->dst.u3, sizeof(t->dst.u3)) &&
     78 	       km->tuple.dst.protonum == t->dst.protonum &&
     79 	       km->tuple.dst.u.all == t->dst.u.all;
     80 }
     81 
     82 /* look up the source key for a given tuple */
     83 static __be16 gre_keymap_lookup(struct net *net, struct nf_conntrack_tuple *t)
     84 {
     85 	struct nf_gre_net *net_gre = gre_pernet(net);
     86 	struct nf_ct_gre_keymap *km;
     87 	__be16 key = 0;
     88 
     89 	list_for_each_entry_rcu(km, &net_gre->keymap_list, list) {
     90 		if (gre_key_cmpfn(km, t)) {
     91 			key = km->tuple.src.u.gre.key;
     92 			break;
     93 		}
     94 	}
     95 
     96 	pr_debug("lookup src key 0x%x for ", key);
     97 	nf_ct_dump_tuple(t);
     98 
     99 	return key;
    100 }
    101 
    102 /* add a single keymap entry, associate with specified master ct */
    103 int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir,
    104 			 struct nf_conntrack_tuple *t)
    105 {
    106 	struct net *net = nf_ct_net(ct);
    107 	struct nf_gre_net *net_gre = gre_pernet(net);
    108 	struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
    109 	struct nf_ct_gre_keymap **kmp, *km;
    110 
    111 	kmp = &ct_pptp_info->keymap[dir];
    112 	if (*kmp) {
    113 		/* check whether it's a retransmission */
    114 		list_for_each_entry_rcu(km, &net_gre->keymap_list, list) {
    115 			if (gre_key_cmpfn(km, t) && km == *kmp)
    116 				return 0;
    117 		}
    118 		pr_debug("trying to override keymap_%s for ct %p\n",
    119 			 dir == IP_CT_DIR_REPLY ? "reply" : "orig", ct);
    120 		return -EEXIST;
    121 	}
    122 
    123 	km = kmalloc(sizeof(*km), GFP_ATOMIC);
    124 	if (!km)
    125 		return -ENOMEM;
    126 	memcpy(&km->tuple, t, sizeof(*t));
    127 	*kmp = km;
    128 
    129 	pr_debug("adding new entry %p: ", km);
    130 	nf_ct_dump_tuple(&km->tuple);
    131 
    132 	spin_lock_bh(&keymap_lock);
    133 	list_add_tail(&km->list, &net_gre->keymap_list);
    134 	spin_unlock_bh(&keymap_lock);
    135 
    136 	return 0;
    137 }
    138 EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_add);
    139 
    140 /* destroy the keymap entries associated with specified master ct */
    141 void nf_ct_gre_keymap_destroy(struct nf_conn *ct)
    142 {
    143 	struct nf_ct_pptp_master *ct_pptp_info = nfct_help_data(ct);
    144 	enum ip_conntrack_dir dir;
    145 
    146 	pr_debug("entering for ct %p\n", ct);
    147 
    148 	spin_lock_bh(&keymap_lock);
    149 	for (dir = IP_CT_DIR_ORIGINAL; dir < IP_CT_DIR_MAX; dir++) {
    150 		if (ct_pptp_info->keymap[dir]) {
    151 			pr_debug("removing %p from list\n",
    152 				 ct_pptp_info->keymap[dir]);
    153 			list_del_rcu(&ct_pptp_info->keymap[dir]->list);
    154 			kfree_rcu(ct_pptp_info->keymap[dir], rcu);
    155 			ct_pptp_info->keymap[dir] = NULL;
    156 		}
    157 	}
    158 	spin_unlock_bh(&keymap_lock);
    159 }
    160 EXPORT_SYMBOL_GPL(nf_ct_gre_keymap_destroy);
    161 
    162 /* PUBLIC CONNTRACK PROTO HELPER FUNCTIONS */
    163 
    164 /* gre hdr info to tuple */
    165 bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
    166 		      struct net *net, struct nf_conntrack_tuple *tuple)
    167 {
    168 	const struct pptp_gre_header *pgrehdr;
    169 	struct pptp_gre_header _pgrehdr;
    170 	__be16 srckey;
    171 	const struct gre_base_hdr *grehdr;
    172 	struct gre_base_hdr _grehdr;
    173 
    174 	/* first only delinearize old RFC1701 GRE header */
    175 	grehdr = skb_header_pointer(skb, dataoff, sizeof(_grehdr), &_grehdr);
    176 	if (!grehdr || (grehdr->flags & GRE_VERSION) != GRE_VERSION_1) {
    177 		/* try to behave like "nf_conntrack_proto_generic" */
    178 		tuple->src.u.all = 0;
    179 		tuple->dst.u.all = 0;
    180 		return true;
    181 	}
    182 
    183 	/* PPTP header is variable length, only need up to the call_id field */
    184 	pgrehdr = skb_header_pointer(skb, dataoff, 8, &_pgrehdr);
    185 	if (!pgrehdr)
    186 		return true;
    187 
    188 	if (grehdr->protocol != GRE_PROTO_PPP) {
    189 		pr_debug("Unsupported GRE proto(0x%x)\n", ntohs(grehdr->protocol));
    190 		return false;
    191 	}
    192 
    193 	tuple->dst.u.gre.key = pgrehdr->call_id;
    194 	srckey = gre_keymap_lookup(net, tuple);
    195 	tuple->src.u.gre.key = srckey;
    196 
    197 	return true;
    198 }
    199 
    200 #ifdef CONFIG_NF_CONNTRACK_PROCFS
    201 /* print private data for conntrack */
    202 static void gre_print_conntrack(struct seq_file *s, struct nf_conn *ct)
    203 {
    204 	seq_printf(s, "timeout=%u, stream_timeout=%u ",
    205 		   (ct->proto.gre.timeout / HZ),
    206 		   (ct->proto.gre.stream_timeout / HZ));
    207 }
    208 #endif
    209 
    210 static unsigned int *gre_get_timeouts(struct net *net)
    211 {
    212 	return gre_pernet(net)->timeouts;
    213 }
    214 
    215 /* Returns verdict for packet, and may modify conntrack */
    216 int nf_conntrack_gre_packet(struct nf_conn *ct,
    217 			    struct sk_buff *skb,
    218 			    unsigned int dataoff,
    219 			    enum ip_conntrack_info ctinfo,
    220 			    const struct nf_hook_state *state)
    221 {
    222 	if (state->pf != NFPROTO_IPV4)
    223 		return -NF_ACCEPT;
    224 
    225 	if (!nf_ct_is_confirmed(ct)) {
    226 		unsigned int *timeouts = nf_ct_timeout_lookup(ct);
    227 
    228 		if (!timeouts)
    229 			timeouts = gre_get_timeouts(nf_ct_net(ct));
    230 
    231 		/* initialize to sane value.  Ideally a conntrack helper
    232 		 * (e.g. in case of pptp) is increasing them */
    233 		ct->proto.gre.stream_timeout = timeouts[GRE_CT_REPLIED];
    234 		ct->proto.gre.timeout = timeouts[GRE_CT_UNREPLIED];
    235 	}
    236 
    237 	/* If we've seen traffic both ways, this is a GRE connection.
    238 	 * Extend timeout. */
    239 	if (ct->status & IPS_SEEN_REPLY) {
    240 		nf_ct_refresh_acct(ct, ctinfo, skb,
    241 				   ct->proto.gre.stream_timeout);
    242 		/* Also, more likely to be important, and not a probe. */
    243 		if (!test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
    244 			nf_conntrack_event_cache(IPCT_ASSURED, ct);
    245 	} else
    246 		nf_ct_refresh_acct(ct, ctinfo, skb,
    247 				   ct->proto.gre.timeout);
    248 
    249 	return NF_ACCEPT;
    250 }
    251 
    252 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
    253 
    254 #include <linux/netfilter/nfnetlink.h>
    255 #include <linux/netfilter/nfnetlink_cttimeout.h>
    256 
    257 static int gre_timeout_nlattr_to_obj(struct nlattr *tb[],
    258 				     struct net *net, void *data)
    259 {
    260 	unsigned int *timeouts = data;
    261 	struct nf_gre_net *net_gre = gre_pernet(net);
    262 
    263 	if (!timeouts)
    264 		timeouts = gre_get_timeouts(net);
    265 	/* set default timeouts for GRE. */
    266 	timeouts[GRE_CT_UNREPLIED] = net_gre->timeouts[GRE_CT_UNREPLIED];
    267 	timeouts[GRE_CT_REPLIED] = net_gre->timeouts[GRE_CT_REPLIED];
    268 
    269 	if (tb[CTA_TIMEOUT_GRE_UNREPLIED]) {
    270 		timeouts[GRE_CT_UNREPLIED] =
    271 			ntohl(nla_get_be32(tb[CTA_TIMEOUT_GRE_UNREPLIED])) * HZ;
    272 	}
    273 	if (tb[CTA_TIMEOUT_GRE_REPLIED]) {
    274 		timeouts[GRE_CT_REPLIED] =
    275 			ntohl(nla_get_be32(tb[CTA_TIMEOUT_GRE_REPLIED])) * HZ;
    276 	}
    277 	return 0;
    278 }
    279 
    280 static int
    281 gre_timeout_obj_to_nlattr(struct sk_buff *skb, const void *data)
    282 {
    283 	const unsigned int *timeouts = data;
    284 
    285 	if (nla_put_be32(skb, CTA_TIMEOUT_GRE_UNREPLIED,
    286 			 htonl(timeouts[GRE_CT_UNREPLIED] / HZ)) ||
    287 	    nla_put_be32(skb, CTA_TIMEOUT_GRE_REPLIED,
    288 			 htonl(timeouts[GRE_CT_REPLIED] / HZ)))
    289 		goto nla_put_failure;
    290 	return 0;
    291 
    292 nla_put_failure:
    293 	return -ENOSPC;
    294 }
    295 
    296 static const struct nla_policy
    297 gre_timeout_nla_policy[CTA_TIMEOUT_GRE_MAX+1] = {
    298 	[CTA_TIMEOUT_GRE_UNREPLIED]	= { .type = NLA_U32 },
    299 	[CTA_TIMEOUT_GRE_REPLIED]	= { .type = NLA_U32 },
    300 };
    301 #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
    302 
    303 void nf_conntrack_gre_init_net(struct net *net)
    304 {
    305 	struct nf_gre_net *net_gre = gre_pernet(net);
    306 	int i;
    307 
    308 	INIT_LIST_HEAD(&net_gre->keymap_list);
    309 	for (i = 0; i < GRE_CT_MAX; i++)
    310 		net_gre->timeouts[i] = gre_timeouts[i];
    311 }
    312 
    313 /* protocol helper struct */
    314 const struct nf_conntrack_l4proto nf_conntrack_l4proto_gre = {
    315 	.l4proto	 = IPPROTO_GRE,
    316 #ifdef CONFIG_NF_CONNTRACK_PROCFS
    317 	.print_conntrack = gre_print_conntrack,
    318 #endif
    319 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
    320 	.tuple_to_nlattr = nf_ct_port_tuple_to_nlattr,
    321 	.nlattr_tuple_size = nf_ct_port_nlattr_tuple_size,
    322 	.nlattr_to_tuple = nf_ct_port_nlattr_to_tuple,
    323 	.nla_policy	 = nf_ct_port_nla_policy,
    324 #endif
    325 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
    326 	.ctnl_timeout    = {
    327 		.nlattr_to_obj	= gre_timeout_nlattr_to_obj,
    328 		.obj_to_nlattr	= gre_timeout_obj_to_nlattr,
    329 		.nlattr_max	= CTA_TIMEOUT_GRE_MAX,
    330 		.obj_size	= sizeof(unsigned int) * GRE_CT_MAX,
    331 		.nla_policy	= gre_timeout_nla_policy,
    332 	},
    333 #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
    334 };