Example #1
0
static unsigned int mangle_packet(struct sk_buff *skb, unsigned int protoff,
                                  unsigned int dataoff,
                                  const char **dptr, unsigned int *datalen,
                                  unsigned int matchoff, unsigned int matchlen,
                                  const char *buffer, unsigned int buflen)
{
    enum ip_conntrack_info ctinfo;
    struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
    struct tcphdr *th;
    unsigned int baseoff;

    if (nf_ct_protonum(ct) == IPPROTO_TCP) {
        th = (struct tcphdr *)(skb->data + protoff);
        baseoff = protoff + th->doff * 4;
        matchoff += dataoff - baseoff;

        if (!__nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
                                        protoff, matchoff, matchlen,
                                        buffer, buflen, false))
            return 0;
    } else {
        baseoff = protoff + sizeof(struct udphdr);
        matchoff += dataoff - baseoff;

        if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo,
                                      protoff, matchoff, matchlen,
                                      buffer, buflen))
            return 0;
    }

    /* Reload data pointer and adjust datalen value */
    *dptr = skb->data + dataoff;
    *datalen += buflen - matchlen;
    return 1;
}
Example #2
0
static int kill_l4proto(struct nf_conn *i, void *data)
{
    struct nf_conntrack_l4proto *l4proto;
    l4proto = (struct nf_conntrack_l4proto *)data;
    return nf_ct_protonum(i) == l4proto->l4proto &&
           nf_ct_l3num(i) == l4proto->l3proto;
}
Example #3
0
static inline int
ctnetlink_dump_protoinfo(struct sk_buff *skb, const struct nf_conn *ct)
{
	struct nf_conntrack_l4proto *l4proto;
	struct nlattr *nest_proto;
	int ret;

	l4proto = nf_ct_l4proto_find_get(nf_ct_l3num(ct), nf_ct_protonum(ct));
	if (!l4proto->to_nlattr) {
		nf_ct_l4proto_put(l4proto);
		return 0;
	}

	nest_proto = nla_nest_start(skb, CTA_PROTOINFO | NLA_F_NESTED);
	if (!nest_proto)
		goto nla_put_failure;

	ret = l4proto->to_nlattr(skb, nest_proto, ct);

	nf_ct_l4proto_put(l4proto);

	nla_nest_end(skb, nest_proto);

	return ret;

nla_put_failure:
	nf_ct_l4proto_put(l4proto);
	return -1;
}
Example #4
0
static
struct nf_conn_colo *nfct_create_colo(struct nf_conn *ct, u32 vm_pid, u32 flag)
{
	struct nf_conn_colo *conn = NULL;
	size_t length = 0;

	if (nf_ct_is_confirmed(ct)) {
		pr_dbg("conntrack %p is confirmed!\n", ct);
		//return NULL;
	}

	if (nf_ct_protonum(ct) == IPPROTO_TCP) {
		length = sizeof(union nf_conn_colo_tcp);

		if (flag & COLO_CONN_SECONDARY) {
			/* seq adjust is only meaningful for TCP conn */
			if (!nfct_seqadj_ext_add(ct)) {
				pr_dbg("failed to add SEQADJ extension\n");
			}
		}
	}

	conn = (struct nf_conn_colo *) nf_ct_ext_add_length(ct, NF_CT_EXT_COLO,
							    length, GFP_ATOMIC);
	if (!conn) {
		pr_dbg("add extend failed\n");
		return NULL;
	}

	conn->nfct = &ct->ct_general;

	return conn;
}
static inline bool already_closed(const struct nf_conn *conn)
{
	if (nf_ct_protonum(conn) == IPPROTO_TCP)
		return conn->proto.tcp.state == TCP_CONNTRACK_TIME_WAIT ||
		       conn->proto.tcp.state == TCP_CONNTRACK_CLOSE;
	else
		return 0;
}
void nf_ct_tcp_seqadj_set(struct sk_buff *skb,
			  struct nf_conn *ct, enum ip_conntrack_info ctinfo,
			  s32 off)
{
	const struct tcphdr *th;

	if (nf_ct_protonum(ct) != IPPROTO_TCP)
		return;

	th = (struct tcphdr *)(skb_network_header(skb) + ip_hdrlen(skb));
	nf_ct_seqadj_set(ct, ctinfo, th->seq, off);
}
Example #7
0
static void ip_nat_sip_seq_adjust(struct sk_buff *skb, s16 off)
{
	enum ip_conntrack_info ctinfo;
	struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
	const struct tcphdr *th;

	if (nf_ct_protonum(ct) != IPPROTO_TCP || off == 0)
		return;

	th = (struct tcphdr *)(skb->data + ip_hdrlen(skb));
	nf_nat_set_seq_adjust(ct, ctinfo, th->seq, off);
}
Example #8
0
/* kill conntracks with affected NAT section */
static int nf_nat_proto_remove(struct nf_conn *i, void *data)
{
	const struct nf_nat_proto_clean *clean = data;
	struct nf_conn_nat *nat = nfct_nat(i);

	if (!nat)
		return 0;

	if ((clean->l3proto && nf_ct_l3num(i) != clean->l3proto) ||
	    (clean->l4proto && nf_ct_protonum(i) != clean->l4proto))
		return 0;

	return i->status & IPS_NAT_MASK ? 1 : 0;
}
static unsigned int fp_netfilter_pre_routing(int family, unsigned int hooknum, struct sk_buff *skb)
{
	struct nf_conn *ct;
	u_int8_t protonum;
	enum ip_conntrack_info ctinfo;
	struct comcerto_fp_info *fp_info;
	int dir;

	ct = nf_ct_get(skb, &ctinfo);
	if (!ct)
		goto done;

	protonum = nf_ct_protonum(ct);
	if ((protonum != IPPROTO_TCP) && (protonum != IPPROTO_UDP))
		goto done;

	dir = CTINFO2DIR(ctinfo);

//	if (printk_ratelimit())
//		printk(KERN_INFO "ct: %lx, dir: %x, mark: %x, ifindex: %d iif: %d\n", (unsigned long)ct, dir, skb->mark, skb->dev->ifindex, skb->skb_iif);

	/* We could also check for changes and notify userspace (or print message) */
	if (dir == IP_CT_DIR_ORIGINAL) {
		fp_info = &ct->fp_info[IP_CT_DIR_ORIGINAL];
	} else {
		fp_info = &ct->fp_info[IP_CT_DIR_REPLY];
	}

	if (fp_info->mark && (fp_info->mark != skb->mark))
		if (printk_ratelimit())
			printk(KERN_INFO "ct: mark changed %x, %x\n", fp_info->mark, skb->mark);

	if (fp_info->ifindex && (fp_info->ifindex != skb->dev->ifindex))
		if (printk_ratelimit())
			printk(KERN_INFO "ct: ifindex changed %d, %d\n", fp_info->ifindex, skb->dev->ifindex);

	if (fp_info->iif && (fp_info->iif != skb->skb_iif))
		if (printk_ratelimit())
			printk(KERN_INFO "ct: iif changed %d, %d\n", fp_info->iif, skb->skb_iif);

	fp_info->mark = skb->mark;
	fp_info->ifindex = skb->dev->ifindex;
	fp_info->iif = skb->skb_iif;

done:
	return NF_ACCEPT;
}
Example #10
0
static int nfnetlink_parse_nat_proto(struct nlattr *attr,
				     const struct nf_conn *ct,
				     struct nf_nat_range *range)
{
	struct nlattr *tb[CTA_PROTONAT_MAX+1];
	const struct nf_nat_l4proto *l4proto;
	int err;

	err = nla_parse_nested(tb, CTA_PROTONAT_MAX, attr, protonat_nla_policy);
	if (err < 0)
		return err;

	l4proto = __nf_nat_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
	if (l4proto->nlattr_to_range)
		err = l4proto->nlattr_to_range(tb, range);

	return err;
}
void nf_ct_l4proto_log_invalid(const struct sk_buff *skb,
			       const struct nf_conn *ct,
			       const char *fmt, ...)
{
	struct va_format vaf;
	struct net *net;
	va_list args;

	net = nf_ct_net(ct);
	if (likely(net->ct.sysctl_log_invalid == 0))
		return;

	va_start(args, fmt);
	vaf.fmt = fmt;
	vaf.va = &args;

	nf_l4proto_log_invalid(skb, net, nf_ct_l3num(ct),
			       nf_ct_protonum(ct), "%pV", &vaf);
	va_end(args);
}
Example #12
0
static int
hmark_ct_set_htuple(const struct sk_buff *skb, struct hmark_tuple *t,
		    const struct xt_hmark_info *info)
{
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
	enum ip_conntrack_info ctinfo;
	struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
	struct nf_conntrack_tuple *otuple;
	struct nf_conntrack_tuple *rtuple;

	if (ct == NULL || nf_ct_is_untracked(ct))
		return -1;

	otuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
	rtuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;

	t->src = hmark_addr_mask(otuple->src.l3num, otuple->src.u3.all,
				 info->src_mask.all);
	t->dst = hmark_addr_mask(otuple->src.l3num, rtuple->src.u3.all,
				 info->dst_mask.all);

	if (info->flags & XT_HMARK_FLAG(XT_HMARK_METHOD_L3))
		return 0;

	t->proto = nf_ct_protonum(ct);
	if (t->proto != IPPROTO_ICMP) {
		t->uports.p16.src = otuple->src.u.all;
		t->uports.p16.dst = rtuple->src.u.all;
		t->uports.v32 = (t->uports.v32 & info->port_mask.v32) |
				info->port_set.v32;
		if (t->uports.p16.dst < t->uports.p16.src)
			swap(t->uports.p16.dst, t->uports.p16.src);
	}

	return 0;
#else
	return -1;
#endif
}
Example #13
0
static inline bool
ct_proto_port_check(const struct xt_conntrack_mtinfo1 *info,
                    const struct nf_conn *ct)
{
    const struct nf_conntrack_tuple *tuple;

    tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
    if ((info->match_flags & XT_CONNTRACK_PROTO) &&
        (nf_ct_protonum(ct) == info->l4proto) ^
        !(info->invert_flags & XT_CONNTRACK_PROTO))
        return false;

    /* Shortcut to match all recognized protocols by using ->src.all. */
    if ((info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) &&
        (tuple->src.u.all == info->origsrc_port) ^
        !(info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT))
        return false;

    if ((info->match_flags & XT_CONNTRACK_ORIGDST_PORT) &&
        (tuple->dst.u.all == info->origdst_port) ^
        !(info->invert_flags & XT_CONNTRACK_ORIGDST_PORT))
        return false;

    tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;

    if ((info->match_flags & XT_CONNTRACK_REPLSRC_PORT) &&
        (tuple->src.u.all == info->replsrc_port) ^
        !(info->invert_flags & XT_CONNTRACK_REPLSRC_PORT))
        return false;

    if ((info->match_flags & XT_CONNTRACK_REPLDST_PORT) &&
        (tuple->dst.u.all == info->repldst_port) ^
        !(info->invert_flags & XT_CONNTRACK_REPLDST_PORT))
        return false;

    return true;
}
Example #14
0
static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff,
			       const char **dptr, unsigned int *datalen)
{
	enum ip_conntrack_info ctinfo;
	struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
	unsigned int coff, matchoff, matchlen;
	enum sip_header_types hdr;
	union nf_inet_addr addr;
	__be16 port;
	int request, in_header;

	/* Basic rules: requests and responses. */
	if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) {
		if (ct_sip_parse_request(ct, *dptr, *datalen,
					 &matchoff, &matchlen,
					 &addr, &port) > 0 &&
		    !map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
			      &addr, port))
			return NF_DROP;
		request = 1;
	} else
		request = 0;

	if (nf_ct_protonum(ct) == IPPROTO_TCP)
		hdr = SIP_HDR_VIA_TCP;
	else
		hdr = SIP_HDR_VIA_UDP;

	/* Translate topmost Via header and parameters */
	if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
				    hdr, NULL, &matchoff, &matchlen,
				    &addr, &port) > 0) {
<<<<<<< HEAD
		unsigned int matchend, poff, plen, buflen, n;
=======
<<<<<<< HEAD
Example #15
0
static void nfct_init_colo(struct nf_conn_colo *conn,
			   u32 vm_pid, u32 flag)
{
	union nf_conn_colo_tcp *proto = NULL;

	if (nf_ct_protonum((struct nf_conn *)conn->nfct) == IPPROTO_TCP) {
		proto = (union nf_conn_colo_tcp *) conn->proto;

		memset(proto, 0, sizeof(*proto));

		if (flag & COLO_CONN_PRIMARY) {
			u32 rcv_nxt = 0;
			u32 max_ack = 0;

			proto->p.compared_seq = proto->p.mrcv_nxt =
			proto->p.srcv_nxt = rcv_nxt;
			proto->p.mack = proto->p.sack = max_ack;
			proto->p.sort = false;
			proto->p.mscale = proto->p.sscale = 1;
			pr_dbg("conn %p nfct_init_colo compared_seq %u, mrnxt %u, srnxt %u, mack %u, sack %u\n",
				conn, proto->p.compared_seq, proto->p.mrcv_nxt,
				proto->p.srcv_nxt, proto->p.mack, proto->p.sack);
		} else {
			proto->s.sec_isn = proto->s.pri_isn = 0;
		}
	}

	skb_queue_head_init(&conn->slaver_pkt_queue);
	INIT_LIST_HEAD(&conn->entry_list);

	INIT_LIST_HEAD(&conn->conn_list);
	spin_lock_init(&conn->lock);
	spin_lock_init(&conn->chk_lock);
	conn->flags |= flag;
	conn->vm_pid = vm_pid;
}
Example #16
0
static void nft_ct_get_eval(const struct nft_expr *expr,
                            struct nft_regs *regs,
                            const struct nft_pktinfo *pkt)
{
    const struct nft_ct *priv = nft_expr_priv(expr);
    u32 *dest = &regs->data[priv->dreg];
    enum ip_conntrack_info ctinfo;
    const struct nf_conn *ct;
    const struct nf_conn_help *help;
    const struct nf_conntrack_tuple *tuple;
    const struct nf_conntrack_helper *helper;
    unsigned int state;

    ct = nf_ct_get(pkt->skb, &ctinfo);

    switch (priv->key) {
    case NFT_CT_STATE:
        if (ct == NULL)
            state = NF_CT_STATE_INVALID_BIT;
        else if (nf_ct_is_untracked(ct))
            state = NF_CT_STATE_UNTRACKED_BIT;
        else
            state = NF_CT_STATE_BIT(ctinfo);
        *dest = state;
        return;
    default:
        break;
    }

    if (ct == NULL)
        goto err;

    switch (priv->key) {
    case NFT_CT_DIRECTION:
        *dest = CTINFO2DIR(ctinfo);
        return;
    case NFT_CT_STATUS:
        *dest = ct->status;
        return;
#ifdef CONFIG_NF_CONNTRACK_MARK
    case NFT_CT_MARK:
        *dest = ct->mark;
        return;
#endif
#ifdef CONFIG_NF_CONNTRACK_SECMARK
    case NFT_CT_SECMARK:
        *dest = ct->secmark;
        return;
#endif
    case NFT_CT_EXPIRATION:
        *dest = jiffies_to_msecs(nf_ct_expires(ct));
        return;
    case NFT_CT_HELPER:
        if (ct->master == NULL)
            goto err;
        help = nfct_help(ct->master);
        if (help == NULL)
            goto err;
        helper = rcu_dereference(help->helper);
        if (helper == NULL)
            goto err;
        strncpy((char *)dest, helper->name, NF_CT_HELPER_NAME_LEN);
        return;
#ifdef CONFIG_NF_CONNTRACK_LABELS
    case NFT_CT_LABELS: {
        struct nf_conn_labels *labels = nf_ct_labels_find(ct);
        unsigned int size;

        if (!labels) {
            memset(dest, 0, NF_CT_LABELS_MAX_SIZE);
            return;
        }

        size = labels->words * sizeof(long);
        memcpy(dest, labels->bits, size);
        if (size < NF_CT_LABELS_MAX_SIZE)
            memset(((char *) dest) + size, 0,
                   NF_CT_LABELS_MAX_SIZE - size);
        return;
    }
#endif
    case NFT_CT_BYTES: /* fallthrough */
    case NFT_CT_PKTS: {
        const struct nf_conn_acct *acct = nf_conn_acct_find(ct);
        u64 count = 0;

        if (acct)
            count = nft_ct_get_eval_counter(acct->counter,
                                            priv->key, priv->dir);
        memcpy(dest, &count, sizeof(count));
        return;
    }
    default:
        break;
    }

    tuple = &ct->tuplehash[priv->dir].tuple;
    switch (priv->key) {
    case NFT_CT_L3PROTOCOL:
        *dest = nf_ct_l3num(ct);
        return;
    case NFT_CT_SRC:
        memcpy(dest, tuple->src.u3.all,
               nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
        return;
    case NFT_CT_DST:
        memcpy(dest, tuple->dst.u3.all,
               nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
        return;
    case NFT_CT_PROTOCOL:
        *dest = nf_ct_protonum(ct);
        return;
    case NFT_CT_PROTO_SRC:
        *dest = (__force __u16)tuple->src.u.all;
        return;
    case NFT_CT_PROTO_DST:
        *dest = (__force __u16)tuple->dst.u.all;
        return;
    default:
        break;
    }
    return;
err:
    regs->verdict.code = NFT_BREAK;
}
Example #17
0
static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff,
			       const char **dptr, unsigned int *datalen)
{
	enum ip_conntrack_info ctinfo;
	struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
	unsigned int coff, matchoff, matchlen;
	enum sip_header_types hdr;
	union nf_inet_addr addr;
	__be16 port;
	int request, in_header;

	/* Basic rules: requests and responses. */
	if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) {
		if (ct_sip_parse_request(ct, *dptr, *datalen,
					 &matchoff, &matchlen,
					 &addr, &port) > 0 &&
		    !map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
			      &addr, port))
			return NF_DROP;
		request = 1;
	} else
		request = 0;

	if (nf_ct_protonum(ct) == IPPROTO_TCP)
		hdr = SIP_HDR_VIA_TCP;
	else
		hdr = SIP_HDR_VIA_UDP;

	/* Translate topmost Via header and parameters */
	if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
				    hdr, NULL, &matchoff, &matchlen,
				    &addr, &port) > 0) {
		unsigned int matchend, poff, plen, buflen, n;
		char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];

		/* We're only interested in headers related to this
		 * connection */
		if (request) {
			if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip ||
			    port != ct->tuplehash[dir].tuple.src.u.udp.port)
				goto next;
		} else {
			if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip ||
			    port != ct->tuplehash[dir].tuple.dst.u.udp.port)
				goto next;
		}

		if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
			      &addr, port))
			return NF_DROP;

		matchend = matchoff + matchlen;

		/* The maddr= parameter (RFC 2361) specifies where to send
		 * the reply. */
		if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
					       "maddr=", &poff, &plen,
					       &addr) > 0 &&
		    addr.ip == ct->tuplehash[dir].tuple.src.u3.ip &&
		    addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) {
			buflen = sprintf(buffer, "%pI4",
					&ct->tuplehash[!dir].tuple.dst.u3.ip);
			if (!mangle_packet(skb, dataoff, dptr, datalen,
					   poff, plen, buffer, buflen))
				return NF_DROP;
		}

		/* The received= parameter (RFC 2361) contains the address
		 * from which the server received the request. */
		if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
					       "received=", &poff, &plen,
					       &addr) > 0 &&
		    addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip &&
		    addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) {
			buflen = sprintf(buffer, "%pI4",
					&ct->tuplehash[!dir].tuple.src.u3.ip);
			if (!mangle_packet(skb, dataoff, dptr, datalen,
					   poff, plen, buffer, buflen))
				return NF_DROP;
		}

		/* The rport= parameter (RFC 3581) contains the port number
		 * from which the server received the request. */
		if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen,
						 "rport=", &poff, &plen,
						 &n) > 0 &&
		    htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port &&
		    htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) {
			__be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
			buflen = sprintf(buffer, "%u", ntohs(p));
			if (!mangle_packet(skb, dataoff, dptr, datalen,
					   poff, plen, buffer, buflen))
				return NF_DROP;
		}
	}

next:
	/* Translate Contact headers */
	coff = 0;
	in_header = 0;
	while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen,
				       SIP_HDR_CONTACT, &in_header,
				       &matchoff, &matchlen,
				       &addr, &port) > 0) {
		if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
			      &addr, port))
			return NF_DROP;
	}

	if (!map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_FROM) ||
	    !map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_TO))
		return NF_DROP;

	return NF_ACCEPT;
}
Example #18
0
static bool
conntrack_mt_v0(const struct sk_buff *skb, const struct net_device *in,
                const struct net_device *out, const struct xt_match *match,
                const void *matchinfo, int offset, unsigned int protoff,
                bool *hotdrop)
{
    const struct xt_conntrack_info *sinfo = matchinfo;
    const struct nf_conn *ct;
    enum ip_conntrack_info ctinfo;
    unsigned int statebit;

    ct = nf_ct_get(skb, &ctinfo);

#define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & (invflg)))

    if (ct == &nf_conntrack_untracked)
        statebit = XT_CONNTRACK_STATE_UNTRACKED;
    else if (ct)
        statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
    else
        statebit = XT_CONNTRACK_STATE_INVALID;

    if (sinfo->flags & XT_CONNTRACK_STATE) {
        if (ct) {
            if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
                statebit |= XT_CONNTRACK_STATE_SNAT;
            if (test_bit(IPS_DST_NAT_BIT, &ct->status))
                statebit |= XT_CONNTRACK_STATE_DNAT;
        }
        if (FWINV((statebit & sinfo->statemask) == 0,
              XT_CONNTRACK_STATE))
            return false;
    }

    if (ct == NULL) {
        if (sinfo->flags & ~XT_CONNTRACK_STATE)
            return false;
        return true;
    }

    if (sinfo->flags & XT_CONNTRACK_PROTO &&
        FWINV(nf_ct_protonum(ct) !=
          sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum,
          XT_CONNTRACK_PROTO))
        return false;

    if (sinfo->flags & XT_CONNTRACK_ORIGSRC &&
        FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip &
           sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
          sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
          XT_CONNTRACK_ORIGSRC))
        return false;

    if (sinfo->flags & XT_CONNTRACK_ORIGDST &&
        FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip &
           sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
          sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
          XT_CONNTRACK_ORIGDST))
        return false;

    if (sinfo->flags & XT_CONNTRACK_REPLSRC &&
        FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip &
           sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) !=
          sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
          XT_CONNTRACK_REPLSRC))
        return false;

    if (sinfo->flags & XT_CONNTRACK_REPLDST &&
        FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip &
           sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) !=
          sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
          XT_CONNTRACK_REPLDST))
        return false;

    if (sinfo->flags & XT_CONNTRACK_STATUS &&
        FWINV((ct->status & sinfo->statusmask) == 0,
          XT_CONNTRACK_STATUS))
        return false;

    if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
        unsigned long expires = timer_pending(&ct->timeout) ?
                    (ct->timeout.expires - jiffies)/HZ : 0;

        if (FWINV(!(expires >= sinfo->expires_min &&
                expires <= sinfo->expires_max),
              XT_CONNTRACK_EXPIRES))
            return false;
    }
    return true;
#undef FWINV
}
Example #19
0
static unsigned int ip_nat_sip(struct sk_buff *skb, unsigned int dataoff,
			       const char **dptr, unsigned int *datalen)
{
	enum ip_conntrack_info ctinfo;
	struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
	unsigned int coff, matchoff, matchlen;
	enum sip_header_types hdr;
	union nf_inet_addr addr;
	__be16 port;
	int request, in_header;

	
	if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) {
		if (ct_sip_parse_request(ct, *dptr, *datalen,
					 &matchoff, &matchlen,
					 &addr, &port) > 0 &&
		    !map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
			      &addr, port))
			return NF_DROP;
		request = 1;
	} else
		request = 0;

	if (nf_ct_protonum(ct) == IPPROTO_TCP)
		hdr = SIP_HDR_VIA_TCP;
	else
		hdr = SIP_HDR_VIA_UDP;

	
	if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
				    hdr, NULL, &matchoff, &matchlen,
				    &addr, &port) > 0) {
		unsigned int olen, matchend, poff, plen, buflen, n;
		char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];

		if (request) {
			if (addr.ip != ct->tuplehash[dir].tuple.src.u3.ip ||
			    port != ct->tuplehash[dir].tuple.src.u.udp.port)
				goto next;
		} else {
			if (addr.ip != ct->tuplehash[dir].tuple.dst.u3.ip ||
			    port != ct->tuplehash[dir].tuple.dst.u.udp.port)
				goto next;
		}

		olen = *datalen;
		if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
			      &addr, port))
			return NF_DROP;

		matchend = matchoff + matchlen + *datalen - olen;

		if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
					       "maddr=", &poff, &plen,
					       &addr) > 0 &&
		    addr.ip == ct->tuplehash[dir].tuple.src.u3.ip &&
		    addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) {
			buflen = sprintf(buffer, "%pI4",
					&ct->tuplehash[!dir].tuple.dst.u3.ip);
			if (!mangle_packet(skb, dataoff, dptr, datalen,
					   poff, plen, buffer, buflen))
				return NF_DROP;
		}

		if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
					       "received=", &poff, &plen,
					       &addr) > 0 &&
		    addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip &&
		    addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) {
			buflen = sprintf(buffer, "%pI4",
					&ct->tuplehash[!dir].tuple.src.u3.ip);
			if (!mangle_packet(skb, dataoff, dptr, datalen,
					   poff, plen, buffer, buflen))
				return NF_DROP;
		}

		if (ct_sip_parse_numerical_param(ct, *dptr, matchend, *datalen,
						 "rport=", &poff, &plen,
						 &n) > 0 &&
		    htons(n) == ct->tuplehash[dir].tuple.dst.u.udp.port &&
		    htons(n) != ct->tuplehash[!dir].tuple.src.u.udp.port) {
			__be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
			buflen = sprintf(buffer, "%u", ntohs(p));
			if (!mangle_packet(skb, dataoff, dptr, datalen,
					   poff, plen, buffer, buflen))
				return NF_DROP;
		}
	}

next:
	
	coff = 0;
	in_header = 0;
	while (ct_sip_parse_header_uri(ct, *dptr, &coff, *datalen,
				       SIP_HDR_CONTACT, &in_header,
				       &matchoff, &matchlen,
				       &addr, &port) > 0) {
		if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
			      &addr, port))
			return NF_DROP;
	}

	if (!map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_FROM) ||
	    !map_sip_addr(skb, dataoff, dptr, datalen, SIP_HDR_TO))
		return NF_DROP;

	return NF_ACCEPT;
}
/* Called when a new connection for this protocol found. */
static bool generic_new(struct nf_conn *ct, const struct sk_buff *skb,
			unsigned int dataoff, unsigned int *timeouts)
{
	return nf_generic_should_process(nf_ct_protonum(ct));
}
Example #21
0
unsigned int
nf_nat_setup_info(struct nf_conn *ct,
		  const struct nf_nat_range *range,
		  enum nf_nat_manip_type maniptype)
{
	struct nf_conntrack_tuple curr_tuple, new_tuple;

	/* Can't setup nat info for confirmed ct. */
	if (nf_ct_is_confirmed(ct))
		return NF_ACCEPT;

	NF_CT_ASSERT(maniptype == NF_NAT_MANIP_SRC ||
		     maniptype == NF_NAT_MANIP_DST);
	BUG_ON(nf_nat_initialized(ct, maniptype));

	/* What we've got will look like inverse of reply. Normally
	 * this is what is in the conntrack, except for prior
	 * manipulations (future optimization: if num_manips == 0,
	 * orig_tp = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple)
	 */
	nf_ct_invert_tuplepr(&curr_tuple,
			     &ct->tuplehash[IP_CT_DIR_REPLY].tuple);

	get_unique_tuple(&new_tuple, &curr_tuple, range, ct, maniptype);

	if (!nf_ct_tuple_equal(&new_tuple, &curr_tuple)) {
		struct nf_conntrack_tuple reply;

		/* Alter conntrack table so will recognize replies. */
		nf_ct_invert_tuplepr(&reply, &new_tuple);
		nf_conntrack_alter_reply(ct, &reply);

		/* Non-atomic: we own this at the moment. */
		if (maniptype == NF_NAT_MANIP_SRC)
			ct->status |= IPS_SRC_NAT;
		else
			ct->status |= IPS_DST_NAT;

		if (nfct_help(ct))
			if (!nfct_seqadj_ext_add(ct))
				return NF_DROP;
	}

	if (maniptype == NF_NAT_MANIP_SRC) {
		struct nf_nat_conn_key key = {
			.net = nf_ct_net(ct),
			.tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
			.zone = nf_ct_zone(ct),
		};
		int err;

		err = rhltable_insert_key(&nf_nat_bysource_table,
					  &key,
					  &ct->nat_bysource,
					  nf_nat_bysource_params);
		if (err)
			return NF_DROP;
	}

	/* It's done. */
	if (maniptype == NF_NAT_MANIP_DST)
		ct->status |= IPS_DST_NAT_DONE;
	else
		ct->status |= IPS_SRC_NAT_DONE;

	return NF_ACCEPT;
}
EXPORT_SYMBOL(nf_nat_setup_info);

static unsigned int
__nf_nat_alloc_null_binding(struct nf_conn *ct, enum nf_nat_manip_type manip)
{
	/* Force range to this IP; let proto decide mapping for
	 * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED).
	 * Use reply in case it's already been mangled (eg local packet).
	 */
	union nf_inet_addr ip =
		(manip == NF_NAT_MANIP_SRC ?
		ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3 :
		ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3);
	struct nf_nat_range range = {
		.flags		= NF_NAT_RANGE_MAP_IPS,
		.min_addr	= ip,
		.max_addr	= ip,
	};
	return nf_nat_setup_info(ct, &range, manip);
}

unsigned int
nf_nat_alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
{
	return __nf_nat_alloc_null_binding(ct, HOOK2MANIP(hooknum));
}
EXPORT_SYMBOL_GPL(nf_nat_alloc_null_binding);

/* Do packet manipulations according to nf_nat_setup_info. */
unsigned int nf_nat_packet(struct nf_conn *ct,
			   enum ip_conntrack_info ctinfo,
			   unsigned int hooknum,
			   struct sk_buff *skb)
{
	const struct nf_nat_l3proto *l3proto;
	const struct nf_nat_l4proto *l4proto;
	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
	unsigned long statusbit;
	enum nf_nat_manip_type mtype = HOOK2MANIP(hooknum);

	if (mtype == NF_NAT_MANIP_SRC)
		statusbit = IPS_SRC_NAT;
	else
		statusbit = IPS_DST_NAT;

	/* Invert if this is reply dir. */
	if (dir == IP_CT_DIR_REPLY)
		statusbit ^= IPS_NAT_MASK;

	/* Non-atomic: these bits don't change. */
	if (ct->status & statusbit) {
		struct nf_conntrack_tuple target;

		/* We are aiming to look like inverse of other direction. */
		nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);

		l3proto = __nf_nat_l3proto_find(target.src.l3num);
		l4proto = __nf_nat_l4proto_find(target.src.l3num,
						target.dst.protonum);
		if (!l3proto->manip_pkt(skb, 0, l4proto, &target, mtype))
			return NF_DROP;
	}
	return NF_ACCEPT;
}
EXPORT_SYMBOL_GPL(nf_nat_packet);

struct nf_nat_proto_clean {
	u8	l3proto;
	u8	l4proto;
};

/* kill conntracks with affected NAT section */
static int nf_nat_proto_remove(struct nf_conn *i, void *data)
{
	const struct nf_nat_proto_clean *clean = data;

	if ((clean->l3proto && nf_ct_l3num(i) != clean->l3proto) ||
	    (clean->l4proto && nf_ct_protonum(i) != clean->l4proto))
		return 0;

	return i->status & IPS_NAT_MASK ? 1 : 0;
}

static int nf_nat_proto_clean(struct nf_conn *ct, void *data)
{
	if (nf_nat_proto_remove(ct, data))
		return 1;

	if ((ct->status & IPS_SRC_NAT_DONE) == 0)
		return 0;

	/* This netns is being destroyed, and conntrack has nat null binding.
	 * Remove it from bysource hash, as the table will be freed soon.
	 *
	 * Else, when the conntrack is destoyed, nf_nat_cleanup_conntrack()
	 * will delete entry from already-freed table.
	 */
	clear_bit(IPS_SRC_NAT_DONE_BIT, &ct->status);
	rhltable_remove(&nf_nat_bysource_table, &ct->nat_bysource,
			nf_nat_bysource_params);

	/* don't delete conntrack.  Although that would make things a lot
	 * simpler, we'd end up flushing all conntracks on nat rmmod.
	 */
	return 0;
}

static void nf_nat_l4proto_clean(u8 l3proto, u8 l4proto)
{
	struct nf_nat_proto_clean clean = {
		.l3proto = l3proto,
		.l4proto = l4proto,
	};
	struct net *net;

	rtnl_lock();
	for_each_net(net)
		nf_ct_iterate_cleanup(net, nf_nat_proto_remove, &clean, 0, 0);
	rtnl_unlock();
}

static void nf_nat_l3proto_clean(u8 l3proto)
{
	struct nf_nat_proto_clean clean = {
		.l3proto = l3proto,
	};
	struct net *net;

	rtnl_lock();

	for_each_net(net)
		nf_ct_iterate_cleanup(net, nf_nat_proto_remove, &clean, 0, 0);
	rtnl_unlock();
}

/* Protocol registration. */
int nf_nat_l4proto_register(u8 l3proto, const struct nf_nat_l4proto *l4proto)
{
	const struct nf_nat_l4proto **l4protos;
	unsigned int i;
	int ret = 0;

	mutex_lock(&nf_nat_proto_mutex);
	if (nf_nat_l4protos[l3proto] == NULL) {
		l4protos = kmalloc(IPPROTO_MAX * sizeof(struct nf_nat_l4proto *),
				   GFP_KERNEL);
		if (l4protos == NULL) {
			ret = -ENOMEM;
			goto out;
		}

		for (i = 0; i < IPPROTO_MAX; i++)
			RCU_INIT_POINTER(l4protos[i], &nf_nat_l4proto_unknown);

		/* Before making proto_array visible to lockless readers,
		 * we must make sure its content is committed to memory.
		 */
		smp_wmb();

		nf_nat_l4protos[l3proto] = l4protos;
	}

	if (rcu_dereference_protected(
			nf_nat_l4protos[l3proto][l4proto->l4proto],
			lockdep_is_held(&nf_nat_proto_mutex)
			) != &nf_nat_l4proto_unknown) {
		ret = -EBUSY;
		goto out;
	}
	RCU_INIT_POINTER(nf_nat_l4protos[l3proto][l4proto->l4proto], l4proto);
 out:
	mutex_unlock(&nf_nat_proto_mutex);
	return ret;
}
EXPORT_SYMBOL_GPL(nf_nat_l4proto_register);

/* No one stores the protocol anywhere; simply delete it. */
void nf_nat_l4proto_unregister(u8 l3proto, const struct nf_nat_l4proto *l4proto)
{
	mutex_lock(&nf_nat_proto_mutex);
	RCU_INIT_POINTER(nf_nat_l4protos[l3proto][l4proto->l4proto],
			 &nf_nat_l4proto_unknown);
	mutex_unlock(&nf_nat_proto_mutex);
	synchronize_rcu();

	nf_nat_l4proto_clean(l3proto, l4proto->l4proto);
}
static void nft_ct_eval(const struct nft_expr *expr,
			struct nft_data data[NFT_REG_MAX + 1],
			const struct nft_pktinfo *pkt)
{
	const struct nft_ct *priv = nft_expr_priv(expr);
	struct nft_data *dest = &data[priv->dreg];
	enum ip_conntrack_info ctinfo;
	const struct nf_conn *ct;
	const struct nf_conn_help *help;
	const struct nf_conntrack_tuple *tuple;
	const struct nf_conntrack_helper *helper;
	long diff;
	unsigned int state;

	ct = nf_ct_get(pkt->skb, &ctinfo);

	switch (priv->key) {
	case NFT_CT_STATE:
		if (ct == NULL)
			state = NF_CT_STATE_INVALID_BIT;
		else if (nf_ct_is_untracked(ct))
			state = NF_CT_STATE_UNTRACKED_BIT;
		else
			state = NF_CT_STATE_BIT(ctinfo);
		dest->data[0] = state;
		return;
	}

	if (ct == NULL)
		goto err;

	switch (priv->key) {
	case NFT_CT_DIRECTION:
		dest->data[0] = CTINFO2DIR(ctinfo);
		return;
	case NFT_CT_STATUS:
		dest->data[0] = ct->status;
		return;
#ifdef CONFIG_NF_CONNTRACK_MARK
	case NFT_CT_MARK:
		dest->data[0] = ct->mark;
		return;
#endif
#ifdef CONFIG_NF_CONNTRACK_SECMARK
	case NFT_CT_SECMARK:
		dest->data[0] = ct->secmark;
		return;
#endif
	case NFT_CT_EXPIRATION:
		diff = (long)jiffies - (long)ct->timeout.expires;
		if (diff < 0)
			diff = 0;
		dest->data[0] = jiffies_to_msecs(diff);
		return;
	case NFT_CT_HELPER:
		if (ct->master == NULL)
			goto err;
		help = nfct_help(ct->master);
		if (help == NULL)
			goto err;
		helper = rcu_dereference(help->helper);
		if (helper == NULL)
			goto err;
		if (strlen(helper->name) >= sizeof(dest->data))
			goto err;
		strncpy((char *)dest->data, helper->name, sizeof(dest->data));
		return;
	}

	tuple = &ct->tuplehash[priv->dir].tuple;
	switch (priv->key) {
	case NFT_CT_L3PROTOCOL:
		dest->data[0] = nf_ct_l3num(ct);
		return;
	case NFT_CT_SRC:
		memcpy(dest->data, tuple->src.u3.all,
		       nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
		return;
	case NFT_CT_DST:
		memcpy(dest->data, tuple->dst.u3.all,
		       nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
		return;
	case NFT_CT_PROTOCOL:
		dest->data[0] = nf_ct_protonum(ct);
		return;
	case NFT_CT_PROTO_SRC:
		dest->data[0] = (__force __u16)tuple->src.u.all;
		return;
	case NFT_CT_PROTO_DST:
		dest->data[0] = (__force __u16)tuple->dst.u.all;
		return;
	}
	return;
err:
	data[NFT_REG_VERDICT].verdict = NFT_BREAK;
}