Пример #1
0
static int synproxy_tg6_check(const struct xt_tgchk_param *par)
{
	struct synproxy_net *snet = synproxy_pernet(par->net);
	const struct ip6t_entry *e = par->entryinfo;
	int err;

	if (!(e->ipv6.flags & IP6T_F_PROTO) ||
	    e->ipv6.proto != IPPROTO_TCP ||
	    e->ipv6.invflags & XT_INV_PROTO)
		return -EINVAL;

	err = nf_ct_netns_get(par->net, par->family);
	if (err)
		return err;

	if (snet->hook_ref6 == 0) {
		err = nf_register_net_hooks(par->net, ipv6_synproxy_ops,
					    ARRAY_SIZE(ipv6_synproxy_ops));
		if (err) {
			nf_ct_netns_put(par->net, par->family);
			return err;
		}
	}

	snet->hook_ref6++;
	return err;
}
Пример #2
0
static void __net_exit synproxy_net_exit(struct net *net)
{
	struct synproxy_net *snet = synproxy_pernet(net);

	nf_conntrack_free(snet->tmpl);
	synproxy_proc_exit(net);
	free_percpu(snet->stats);
}
Пример #3
0
static void synproxy_tg6_destroy(const struct xt_tgdtor_param *par)
{
	struct synproxy_net *snet = synproxy_pernet(par->net);

	snet->hook_ref6--;
	if (snet->hook_ref6 == 0)
		nf_unregister_net_hooks(par->net, ipv6_synproxy_ops,
					ARRAY_SIZE(ipv6_synproxy_ops));
	nf_ct_netns_put(par->net, par->family);
}
Пример #4
0
static void *synproxy_cpu_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
	struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
	int cpu;

	for (cpu = *pos; cpu < nr_cpu_ids; cpu++) {
		if (!cpu_possible(cpu))
			continue;
		*pos = cpu + 1;
		return per_cpu_ptr(snet->stats, cpu);
	}

	return NULL;
}
Пример #5
0
static unsigned int
synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par)
{
	const struct xt_synproxy_info *info = par->targinfo;
	struct net *net = xt_net(par);
	struct synproxy_net *snet = synproxy_pernet(net);
	struct synproxy_options opts = {};
	struct tcphdr *th, _th;

	if (nf_ip6_checksum(skb, xt_hooknum(par), par->thoff, IPPROTO_TCP))
		return NF_DROP;

	th = skb_header_pointer(skb, par->thoff, sizeof(_th), &_th);
	if (th == NULL)
		return NF_DROP;

	if (!synproxy_parse_options(skb, par->thoff, th, &opts))
		return NF_DROP;

	if (th->syn && !(th->ack || th->fin || th->rst)) {
		/* Initial SYN from client */
		this_cpu_inc(snet->stats->syn_received);

		if (th->ece && th->cwr)
			opts.options |= XT_SYNPROXY_OPT_ECN;

		opts.options &= info->options;
		if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
			synproxy_init_timestamp_cookie(info, &opts);
		else
			opts.options &= ~(XT_SYNPROXY_OPT_WSCALE |
					  XT_SYNPROXY_OPT_SACK_PERM |
					  XT_SYNPROXY_OPT_ECN);

		synproxy_send_client_synack(net, skb, th, &opts);
		consume_skb(skb);
		return NF_STOLEN;

	} else if (th->ack && !(th->fin || th->rst || th->syn)) {
		/* ACK from client */
		if (synproxy_recv_client_ack(net, skb, th, &opts, ntohl(th->seq))) {
			consume_skb(skb);
			return NF_STOLEN;
		} else {
			return NF_DROP;
		}
	}

	return XT_CONTINUE;
}
Пример #6
0
static void *synproxy_cpu_seq_start(struct seq_file *seq, loff_t *pos)
{
	struct synproxy_net *snet = synproxy_pernet(seq_file_net(seq));
	int cpu;

	if (*pos == 0)
		return SEQ_START_TOKEN;

	for (cpu = *pos - 1; cpu < nr_cpu_ids; cpu++) {
		if (!cpu_possible(cpu))
			continue;
		*pos = cpu + 1;
		return per_cpu_ptr(snet->stats, cpu);
	}

	return NULL;
}
Пример #7
0
static void
synproxy_send_server_syn(struct net *net,
                         const struct sk_buff *skb, const struct tcphdr *th,
                         const struct synproxy_options *opts, u32 recv_seq)
{
    struct synproxy_net *snet = synproxy_pernet(net);
    struct sk_buff *nskb;
    struct iphdr *iph, *niph;
    struct tcphdr *nth;
    unsigned int tcp_hdr_size;

    iph = ip_hdr(skb);

    tcp_hdr_size = sizeof(*nth) + synproxy_options_size(opts);
    nskb = alloc_skb(sizeof(*niph) + tcp_hdr_size + MAX_TCP_HEADER,
                     GFP_ATOMIC);
    if (nskb == NULL)
        return;
    skb_reserve(nskb, MAX_TCP_HEADER);

    niph = synproxy_build_ip(net, nskb, iph->saddr, iph->daddr);

    skb_reset_transport_header(nskb);
    nth = (struct tcphdr *)skb_put(nskb, tcp_hdr_size);
    nth->source	= th->source;
    nth->dest	= th->dest;
    nth->seq	= htonl(recv_seq - 1);
    /* ack_seq is used to relay our ISN to the synproxy hook to initialize
     * sequence number translation once a connection tracking entry exists.
     */
    nth->ack_seq	= htonl(ntohl(th->ack_seq) - 1);
    tcp_flag_word(nth) = TCP_FLAG_SYN;
    if (opts->options & XT_SYNPROXY_OPT_ECN)
        tcp_flag_word(nth) |= TCP_FLAG_ECE | TCP_FLAG_CWR;
    nth->doff	= tcp_hdr_size / 4;
    nth->window	= th->window;
    nth->check	= 0;
    nth->urg_ptr	= 0;

    synproxy_build_options(nth, opts);

    synproxy_send_tcp(net, skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW,
                      niph, nth, tcp_hdr_size);
}
Пример #8
0
static int __net_init synproxy_net_init(struct net *net)
{
	struct synproxy_net *snet = synproxy_pernet(net);
	struct nf_conntrack_tuple t;
	struct nf_conn *ct;
	int err = -ENOMEM;

	memset(&t, 0, sizeof(t));
	ct = nf_conntrack_alloc(net, 0, &t, &t, GFP_KERNEL);
	if (IS_ERR(ct)) {
		err = PTR_ERR(ct);
		goto err1;
	}

	if (!nfct_seqadj_ext_add(ct))
		goto err2;
	if (!nfct_synproxy_ext_add(ct))
		goto err2;
	__set_bit(IPS_TEMPLATE_BIT, &ct->status);
	__set_bit(IPS_CONFIRMED_BIT, &ct->status);

	snet->tmpl = ct;

	snet->stats = alloc_percpu(struct synproxy_stats);
	if (snet->stats == NULL)
		goto err2;

	err = synproxy_proc_init(net);
	if (err < 0)
		goto err3;

	return 0;

err3:
	free_percpu(snet->stats);
err2:
	nf_conntrack_free(ct);
err1:
	return err;
}
Пример #9
0
static bool
synproxy_recv_client_ack(struct net *net,
                         const struct sk_buff *skb, const struct tcphdr *th,
                         struct synproxy_options *opts, u32 recv_seq)
{
    struct synproxy_net *snet = synproxy_pernet(net);
    int mss;

    mss = __cookie_v4_check(ip_hdr(skb), th, ntohl(th->ack_seq) - 1);
    if (mss == 0) {
        this_cpu_inc(snet->stats->cookie_invalid);
        return false;
    }

    this_cpu_inc(snet->stats->cookie_valid);
    opts->mss = mss;
    opts->options |= XT_SYNPROXY_OPT_MSS;

    if (opts->options & XT_SYNPROXY_OPT_TIMESTAMP)
        synproxy_check_timestamp_cookie(opts);

    synproxy_send_server_syn(net, skb, th, opts, recv_seq);
    return true;
}
Пример #10
0
static int __net_init synproxy_net_init(struct net *net)
{
	struct synproxy_net *snet = synproxy_pernet(net);
	struct nf_conn *ct;
	int err = -ENOMEM;

	ct = nf_ct_tmpl_alloc(net, &nf_ct_zone_dflt, GFP_KERNEL);
	if (!ct)
		goto err1;

	if (!nfct_seqadj_ext_add(ct))
		goto err2;
	if (!nfct_synproxy_ext_add(ct))
		goto err2;

	__set_bit(IPS_CONFIRMED_BIT, &ct->status);
	nf_conntrack_get(&ct->ct_general);
	snet->tmpl = ct;

	snet->stats = alloc_percpu(struct synproxy_stats);
	if (snet->stats == NULL)
		goto err2;

	err = synproxy_proc_init(net);
	if (err < 0)
		goto err3;

	return 0;

err3:
	free_percpu(snet->stats);
err2:
	nf_ct_tmpl_free(ct);
err1:
	return err;
}
Пример #11
0
static unsigned int ipv4_synproxy_hook(void *priv,
                                       struct sk_buff *skb,
                                       const struct nf_hook_state *nhs)
{
    struct net *net = nhs->net;
    struct synproxy_net *snet = synproxy_pernet(net);
    enum ip_conntrack_info ctinfo;
    struct nf_conn *ct;
    struct nf_conn_synproxy *synproxy;
    struct synproxy_options opts = {};
    const struct ip_ct_tcp *state;
    struct tcphdr *th, _th;
    unsigned int thoff;

    ct = nf_ct_get(skb, &ctinfo);
    if (ct == NULL)
        return NF_ACCEPT;

    synproxy = nfct_synproxy(ct);
    if (synproxy == NULL)
        return NF_ACCEPT;

    if (nf_is_loopback_packet(skb))
        return NF_ACCEPT;

    thoff = ip_hdrlen(skb);
    th = skb_header_pointer(skb, thoff, sizeof(_th), &_th);
    if (th == NULL)
        return NF_DROP;

    state = &ct->proto.tcp;
    switch (state->state) {
    case TCP_CONNTRACK_CLOSE:
        if (th->rst && !test_bit(IPS_SEEN_REPLY_BIT, &ct->status)) {
            nf_ct_seqadj_init(ct, ctinfo, synproxy->isn -
                              ntohl(th->seq) + 1);
            break;
        }

        if (!th->syn || th->ack ||
                CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL)
            break;

        /* Reopened connection - reset the sequence number and timestamp
         * adjustments, they will get initialized once the connection is
         * reestablished.
         */
        nf_ct_seqadj_init(ct, ctinfo, 0);
        synproxy->tsoff = 0;
        this_cpu_inc(snet->stats->conn_reopened);

    /* fall through */
    case TCP_CONNTRACK_SYN_SENT:
        if (!synproxy_parse_options(skb, thoff, th, &opts))
            return NF_DROP;

        if (!th->syn && th->ack &&
                CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) {
            /* Keep-Alives are sent with SEG.SEQ = SND.NXT-1,
             * therefore we need to add 1 to make the SYN sequence
             * number match the one of first SYN.
             */
            if (synproxy_recv_client_ack(net, skb, th, &opts,
                                         ntohl(th->seq) + 1))
                this_cpu_inc(snet->stats->cookie_retrans);

            return NF_DROP;
        }

        synproxy->isn = ntohl(th->ack_seq);
        if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
            synproxy->its = opts.tsecr;
        break;
    case TCP_CONNTRACK_SYN_RECV:
        if (!th->syn || !th->ack)
            break;

        if (!synproxy_parse_options(skb, thoff, th, &opts))
            return NF_DROP;

        if (opts.options & XT_SYNPROXY_OPT_TIMESTAMP)
            synproxy->tsoff = opts.tsval - synproxy->its;

        opts.options &= ~(XT_SYNPROXY_OPT_MSS |
                          XT_SYNPROXY_OPT_WSCALE |
                          XT_SYNPROXY_OPT_SACK_PERM);

        swap(opts.tsval, opts.tsecr);
        synproxy_send_server_ack(net, state, skb, th, &opts);

        nf_ct_seqadj_init(ct, ctinfo, synproxy->isn - ntohl(th->seq));

        swap(opts.tsval, opts.tsecr);
        synproxy_send_client_ack(net, skb, th, &opts);

        consume_skb(skb);
        return NF_STOLEN;
    default:
        break;
    }

    synproxy_tstamp_adjust(skb, thoff, th, ct, ctinfo, synproxy);
    return NF_ACCEPT;
}