예제 #1
0
static int tftp_help(struct sk_buff **pskb,
		     struct ip_conntrack *ct,
		     enum ip_conntrack_info ctinfo)
{
	struct tftphdr _tftph, *tfh;
	struct ip_conntrack_expect *exp;
	unsigned int ret = NF_ACCEPT;

	tfh = skb_header_pointer(*pskb,
				 (*pskb)->nh.iph->ihl*4+sizeof(struct udphdr),
				 sizeof(_tftph), &_tftph);
	if (tfh == NULL)
		return NF_ACCEPT;

	switch (ntohs(tfh->opcode)) {
	/* RRQ and WRQ works the same way */
	case TFTP_OPCODE_READ:
	case TFTP_OPCODE_WRITE:
		DEBUGP("");
		DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
		DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);

		exp = ip_conntrack_expect_alloc(ct);
		if (exp == NULL)
			return NF_DROP;

		exp->tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
		exp->mask.src.ip = 0xffffffff;
		exp->mask.src.u.udp.port = 0;
		exp->mask.dst.ip = 0xffffffff;
		exp->mask.dst.u.udp.port = 0xffff;
		exp->mask.dst.protonum = 0xff;
		exp->expectfn = NULL;
		exp->flags = 0;

		DEBUGP("expect: ");
		DUMP_TUPLE(&exp->tuple);
		DUMP_TUPLE(&exp->mask);
		if (ip_nat_tftp_hook)
			ret = ip_nat_tftp_hook(pskb, ctinfo, exp);
		else if (ip_conntrack_expect_related(exp) != 0)
			ret = NF_DROP;
		ip_conntrack_expect_put(exp);
		break;
	case TFTP_OPCODE_DATA:
	case TFTP_OPCODE_ACK:
		DEBUGP("Data/ACK opcode\n");
		break;
	case TFTP_OPCODE_ERROR:
		DEBUGP("Error opcode\n");
		break;
	default:
		DEBUGP("Unknown opcode\n");
	}
	return NF_ACCEPT;
}
static int timeout_ct_or_exp(const struct ip_conntrack_tuple *t)
{
	struct ip_conntrack_tuple_hash *h;
	struct ip_conntrack_expect *exp;

	DEBUGP("trying to timeout ct or exp for tuple ");
	DUMP_TUPLE(t);

	h = __ip_conntrack_find(t, NULL);
	if (h)  {
		struct ip_conntrack *sibling = tuplehash_to_ctrack(h);
		DEBUGP("setting timeout of conntrack %p to 0\n", sibling);
		sibling->proto.gre.timeout = 0;
		sibling->proto.gre.stream_timeout = 0;
		/* refresh_acct will not modify counters if skb == NULL */
		ip_ct_refresh_acct(sibling, 0, NULL, 0);
		return 1;
	} else {
		exp = __ip_conntrack_exp_find(t);
		if (exp) {
			DEBUGP("unexpect_related of expect %p\n", exp);
			ip_conntrack_unexpect_related(exp);
			return 1;
		}
	}

	return 0;
}
static void pptp_expectfn(struct ip_conntrack *ct,
			 struct ip_conntrack_expect *exp)
{
	DEBUGP("increasing timeouts\n");

	/* increase timeout of GRE data channel conntrack entry */
	ct->proto.gre.timeout = PPTP_GRE_TIMEOUT;
	ct->proto.gre.stream_timeout = PPTP_GRE_STREAM_TIMEOUT;

	/* Can you see how rusty this code is, compared with the pre-2.6.11
	 * one? That's what happened to my shiny newnat of 2002 ;( -HW */

	if (!ip_nat_pptp_hook_expectfn) {
		struct ip_conntrack_tuple inv_t;
		struct ip_conntrack_expect *exp_other;

		/* obviously this tuple inversion only works until you do NAT */
		invert_tuplepr(&inv_t, &exp->tuple);
		DEBUGP("trying to unexpect other dir: ");
		DUMP_TUPLE(&inv_t);
	
		exp_other = __ip_conntrack_exp_find(&inv_t);
		if (exp_other) {
			/* delete other expectation.  */
			DEBUGP("found\n");
			ip_conntrack_unexpect_related(exp_other);
		} else {
			DEBUGP("not found\n");
		}
	} else {
		/* we need more than simple inversion */
		ip_nat_pptp_hook_expectfn(ct, exp);
	}
}
static int destroy_sibling_or_exp(const struct ip_conntrack_tuple *t)
{
	struct ip_conntrack_tuple_hash *h;
	struct ip_conntrack_expect *exp;

	DEBUGP("trying to timeout ct or exp for tuple ");
	DUMP_TUPLE(t);

	h = ip_conntrack_find_get(t, NULL);
	if (h)  {
		struct ip_conntrack *sibling = tuplehash_to_ctrack(h);
		DEBUGP("setting timeout of conntrack %p to 0\n", sibling);
		sibling->proto.gre.timeout = 0;
		sibling->proto.gre.stream_timeout = 0;
		if (del_timer(&sibling->timeout))
			sibling->timeout.function((unsigned long)sibling);
		ip_conntrack_put(sibling);
		return 1;
	} else {
		exp = ip_conntrack_expect_find(t);
		if (exp) {
			DEBUGP("unexpect_related of expect %p\n", exp);
			ip_conntrack_unexpect_related(exp);
			ip_conntrack_expect_put(exp);
			return 1;
		}
	}

	return 0;
}
예제 #5
0
/* FIXME: This should be in userspace.  Later. */
static int help(struct sk_buff *skb,
		struct ip_conntrack *ct,
		enum ip_conntrack_info ctinfo)
{
	int dir = CTINFO2DIR(ctinfo);
	struct tcphdr tcph;
        struct ip_conntrack_expect exp;
	int i;

	/*
	** We only do this for the new packet 
        */
//	printk("wm_help: Conntrackinfo = %u dir=%d\n", ctinfo,dir);
	if ( ctinfo != IP_CT_NEW)
		return NF_ACCEPT;
	if ( dir != 0)
		return NF_ACCEPT;
        //DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
        //DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
	
	if (skb_copy_bits(skb, skb->nh.iph->ihl*4, &tcph, sizeof(tcph)) != 0)
		return NF_ACCEPT;	

	LOCK_BH(&ip_wm_lock);
	//for(i = WMMIN; i <= WMMIN; i++) {
	for(i = WMMIN; i <= WMMAX; i++) {
            memset(&exp, 0, sizeof(exp));
            exp.tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
            exp.tuple.dst.u.udp.port = htons(i);
 	    exp.tuple.dst.protonum = IPPROTO_UDP;
            exp.mask.src.ip = 0xffffffff;
            exp.mask.dst.ip = 0xffffffff;
            exp.mask.dst.u.udp.port = 0xffff;
            exp.mask.dst.protonum = 0xffff;
            exp.expectfn = NULL;
            exp.seq = ntohl(tcph.seq);

            DEBUGP("wm_help: expect: ");
            DUMP_TUPLE(&exp.tuple);
            DUMP_TUPLE(&exp.mask);
            ip_conntrack_expect_related(&exp, ct);
	}
	UNLOCK_BH(&ip_wm_lock);
        return NF_ACCEPT;
}
예제 #6
0
파일: ip_autofw.c 프로젝트: cilynx/dd-wrt
static unsigned int
autofw_nat_expected(struct sk_buff **pskb,
		 unsigned int hooknum,
		 struct ip_conntrack *ct,
		 struct ip_nat_info *info)
{
	struct ip_nat_multi_range mr;
	u_int32_t newdstip, newsrcip, newip;
	u_int16_t port;
	struct ip_conntrack *master = master_ct(ct);

	IP_NF_ASSERT(info);
	IP_NF_ASSERT(master);

	IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));

	DEBUGP("autofw_nat_expected: got ");
	DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);

	LOCK_BH(&ip_autofw_lock);

	port = ntohs(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.all);
	newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
#ifdef NEW_PORT_TRIG
	newsrcip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
#else
	newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
#endif

	if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
		newip = newsrcip;
	else {
		if (port < ntohs(ct->master->help.exp_autofw_info.dport[0]) ||
		    port > ntohs(ct->master->help.exp_autofw_info.dport[1])) {
			UNLOCK_BH(&ip_autofw_lock);
			return NF_DROP;
		}
		newip = newdstip;
	}

	mr.rangesize = 1;
	/* We don't want to manip the per-protocol, just the IPs... */
	mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
	mr.range[0].min_ip = mr.range[0].max_ip = newip;

	/* ... unless we're doing a MANIP_DST, in which case, make
	   sure we map to the correct port */
	port -= ntohs(ct->master->help.exp_autofw_info.dport[0]);
	port += ntohs(ct->master->help.exp_autofw_info.to[0]);
	if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
		mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
		mr.range[0].min = mr.range[0].max
			= ((union ip_conntrack_manip_proto)
				{ htons(port) });
예제 #7
0
static int tftp_help(const struct iphdr *iph, size_t len,
	struct ip_conntrack *ct,
	enum ip_conntrack_info ctinfo)
{
	struct udphdr *udph = (void *)iph + iph->ihl * 4;
	struct tftphdr *tftph = (void *)udph + 8;
	struct ip_conntrack_expect exp;
	
	switch (ntohs(tftph->opcode)) {
	/* RRQ and WRQ works the same way */
	case TFTP_OPCODE_READ:
	case TFTP_OPCODE_WRITE:
		DEBUGP("");
		DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
		DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
		memset(&exp, 0, sizeof(exp));

		exp.tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
		exp.mask.src.ip = 0xffffffff;
		exp.mask.dst.ip = 0xffffffff;
		exp.mask.dst.u.udp.port = 0xffff;
		exp.mask.dst.protonum = 0xffff;
		exp.expectfn = NULL;

		DEBUGP("expect: ");
		DUMP_TUPLE(&exp.tuple);
		DUMP_TUPLE(&exp.mask);
		ip_conntrack_expect_related(ct, &exp);
		break;
	case TFTP_OPCODE_DATA:
	case TFTP_OPCODE_ACK:
		DEBUGP("Data/ACK opcode\n");
		break;
	case TFTP_OPCODE_ERROR:
		DEBUGP("Error opcode\n");
		break;
	default:
		DEBUGP("Unknown opcode\n");
	}
	return NF_ACCEPT;
}
예제 #8
0
static int tftp_help(struct sk_buff *skb,
		     struct ip_conntrack *ct,
		     enum ip_conntrack_info ctinfo)
{
	struct tftphdr tftph;
	struct ip_conntrack_expect exp;

	if (skb_copy_bits(skb, skb->nh.iph->ihl * 4 + sizeof(struct udphdr),
			  &tftph, sizeof(tftph)) != 0)
		return NF_ACCEPT;

	switch (ntohs(tftph.opcode)) {
	/* RRQ and WRQ works the same way */
	case TFTP_OPCODE_READ:
	case TFTP_OPCODE_WRITE:
		DEBUGP("");
		DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
		DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
		memset(&exp, 0, sizeof(exp));

		exp.tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
		exp.mask.src.ip = 0xffffffff;
		exp.mask.dst.ip = 0xffffffff;
		exp.mask.dst.u.udp.port = 0xffff;
		exp.mask.dst.protonum = 0xffff;
		exp.expectfn = NULL;

		DEBUGP("expect: ");
		DUMP_TUPLE(&exp.tuple);
		DUMP_TUPLE(&exp.mask);
		ip_conntrack_expect_related(ct, &exp);
		break;
	default:
		DEBUGP("Unknown opcode\n");
	}
	return NF_ACCEPT;
}
/* Called when a new connection for this protocol found. */
static int icmp_new(struct ip_conntrack *conntrack,
		    const struct sk_buff *skb)
{
	static const u_int8_t valid_new[] = { 
		[ICMP_ECHO] = 1,
		[ICMP_TIMESTAMP] = 1,
		[ICMP_INFO_REQUEST] = 1,
		[ICMP_ADDRESS] = 1 
	};

	if (conntrack->tuplehash[0].tuple.dst.u.icmp.type >= sizeof(valid_new)
	    || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmp.type]) {
		/* Can't create a new ICMP `conn' with this. */
		DEBUGP("icmp: can't create new conn with type %u\n",
		       conntrack->tuplehash[0].tuple.dst.u.icmp.type);
		DUMP_TUPLE(&conntrack->tuplehash[0].tuple);
		return 0;
	}
	atomic_set(&conntrack->proto.icmp.count, 0);
	return 1;
}
예제 #10
0
static void pptp_nat_expected(struct ip_conntrack *ct,
			      struct ip_conntrack_expect *exp)
{
	struct ip_conntrack *master = ct->master;
	struct ip_conntrack_expect *other_exp;
	struct ip_conntrack_tuple t;
	struct ip_ct_pptp_master *ct_pptp_info;
	struct ip_nat_pptp *nat_pptp_info;
	struct ip_nat_range range;

	ct_pptp_info = &master->help.ct_pptp_info;
	nat_pptp_info = &master->nat.help.nat_pptp_info;

	/* And here goes the grand finale of corrosion... */

	if (exp->dir == IP_CT_DIR_ORIGINAL) {
		DEBUGP("we are PNS->PAC\n");
		/* therefore, build tuple for PAC->PNS */
		t.src.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
		t.src.u.gre.key = htons(master->help.ct_pptp_info.pac_call_id);
		t.dst.ip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
		t.dst.u.gre.key = htons(master->help.ct_pptp_info.pns_call_id);
		t.dst.protonum = IPPROTO_GRE;
	} else {
		DEBUGP("we are PAC->PNS\n");
		/* build tuple for PNS->PAC */
		t.src.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
		t.src.u.gre.key = 
			htons(master->nat.help.nat_pptp_info.pns_call_id);
		t.dst.ip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
		t.dst.u.gre.key = 
			htons(master->nat.help.nat_pptp_info.pac_call_id);
		t.dst.protonum = IPPROTO_GRE;
	}

	DEBUGP("trying to unexpect other dir: \n");
	DUMP_TUPLE(&t);
	other_exp = __ip_conntrack_exp_find(&t);
	if (other_exp) {
		ip_conntrack_unexpect_related(other_exp);
		DEBUGP("success\n");
	} else {
		DEBUGP("not found!\n");
	}

	/* This must be a fresh one. */
	BUG_ON(ct->status & IPS_NAT_DONE_MASK);

	/* Change src to where master sends to */
	range.flags = IP_NAT_RANGE_MAP_IPS;
	range.min_ip = range.max_ip
		= ct->master->tuplehash[!exp->dir].tuple.dst.ip;
	if (exp->dir == IP_CT_DIR_ORIGINAL) {
		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
		range.min = range.max = exp->saved_proto;
	}
	/* hook doesn't matter, but it has to do source manip */
	ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);

	/* For DST manip, map port here to where it's expected. */
	range.flags = IP_NAT_RANGE_MAP_IPS;
	range.min_ip = range.max_ip
		= ct->master->tuplehash[!exp->dir].tuple.src.ip;
//sam add +++ 2008.10.03 for pptp passthrough can not work.
	ct->tuplehash[!exp->dir].tuple.dst.u.gre.key = t.dst.u.gre.key;
//sam add ---	
	if (exp->dir == IP_CT_DIR_REPLY) {
		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
		range.min = range.max = exp->saved_proto;
	}
	/* hook doesn't matter, but it has to do destination manip */
	ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING);
}
예제 #11
0
/* FIXME: This should be in userspace.  Later. */
static int help(const struct iphdr *iph, size_t len,
		struct ip_conntrack *ct,
		enum ip_conntrack_info ctinfo)
{
	//int dir = CTINFO2DIR(ctinfo);
	//struct udphdr *udph = (void *)iph + iph->ihl * 4;
        struct ip_conntrack_expect exp;
	int i;
	u_int16_t port;
	u_int16_t outproto;
	u_int16_t outport;

	//DEBUGP("pt_help: Conntrackinfo = %u dir=%d\n", ctinfo,dir);
        DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
        DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
/*
** We need to search the pt_record[] to see what inports need to be triggered
** based on the tuple's destination protonum and port number 
** we know from the ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple the protonum and port number
** ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum
** ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.all
*/
	//dump_pt_record();
 outproto = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum;
 outport = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.all;
 //printk("ip_conntrack_pt: outproto=%d outport=%hu\n",outproto,ntohs(outport));
	/*convert proto */
	if (outproto == IPPROTO_TCP)
		outproto=1;
	else if (outproto == IPPROTO_UDP)
		outproto=2;
	else
		outproto=0;

        memset(&exp, 0, sizeof(exp));
        exp.tuple = ct->tuplehash[IP_CT_DIR_REPLY].tuple;
	//dump_pt_record();
	for(i = 0; i < entries; i++) {
	  /* we only call expect_related for entries match our outgoing port(outport) */
	  if ((outproto == pt_record[i].outproto || pt_record[i].outproto== 0) && (ntohs(outport) >= pt_record[i].outport[0] && ntohs(outport) <= pt_record[i].outport[1])) { 
	    for (port = pt_record[i].inport[0]; port <= pt_record[i].inport[1];port++) { 
		//printk("******** ip_conntrack_pt: help port=%d \n",port);
		if ( pt_record[i].inproto == 1 ) {
		   //printk("******** ip_conntrack_pt: help TCP port=%d \n",port);
            	   exp.tuple.dst.protonum = IPPROTO_TCP;
            	   exp.tuple.dst.u.tcp.port = htons(port);
            	   //exp.mask.src.ip = 0xffffffff;
            	   exp.mask.src.ip = 0;
            	   exp.mask.src.u.all = 0;
            	   exp.mask.dst.ip = 0xffffffff;
            	   exp.mask.dst.u.tcp.port = 0xffff;
            	   exp.mask.dst.protonum = 0xffff;
		}
		else if ( pt_record[i].inproto == 2 ) {
		   //printk("******** ip_conntrack_pt: help UDP port=%d \n",port);
            	   exp.tuple.dst.u.udp.port = htons(port);
            	   exp.tuple.dst.protonum = IPPROTO_UDP;
            	   //exp.mask.src.ip = 0xffffffff;
            	   exp.mask.src.ip = 0;
            	   exp.mask.src.u.all = 0;
            	   exp.mask.dst.ip = 0xffffffff;
            	   exp.mask.dst.u.udp.port = 0xffff;
            	   exp.mask.dst.protonum = 0xffff;
		}
		else {
		   //printk("******** ip_conntrack_pt: help BOTH port=%x \n",port);
            	   exp.tuple.dst.u.all = htons(port);
            	   //exp.mask.src.ip = 0xffffffff;
            	   exp.mask.src.ip = 0;
            	   exp.mask.src.u.all = 0;
            	   exp.mask.dst.ip = 0xffffffff;
            	   exp.mask.dst.u.all = 0xffff;
            	   exp.mask.dst.protonum = 0;
		}
                exp.expectfn = NULL;

                //DEBUGP("expect: ");
                //DUMP_TUPLE(&exp.tuple);
                //DUMP_TUPLE(&exp.mask);
                ip_conntrack_expect_related(ct, &exp);
	    } /* end of for */
	  } /* end of if */
	}
        return NF_ACCEPT;
}
예제 #12
0
static unsigned int
h225_nat_expected(struct sk_buff **pskb,
		  unsigned int hooknum,
		  struct ip_conntrack *ct,
		  struct ip_nat_info *info)
{
	struct ip_nat_multi_range mr;
	u_int32_t newdstip, newsrcip, newip;
	u_int16_t port;
	struct ip_ct_h225_expect *exp_info;
	struct ip_ct_h225_master *master_info;
	struct ip_conntrack *master = master_ct(ct);
	unsigned int is_h225, ret;
	
	IP_NF_ASSERT(info);
	IP_NF_ASSERT(master);

	IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));

	DEBUGP("h225_nat_expected: We have a connection!\n");
	master_info = &ct->master->expectant->help.ct_h225_info;
	exp_info = &ct->master->help.exp_h225_info;

	LOCK_BH(&ip_h323_lock);

	DEBUGP("master: ");
	DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
	DUMP_TUPLE(&master->tuplehash[IP_CT_DIR_REPLY].tuple);
	DEBUGP("conntrack: ");
	DUMP_TUPLE(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple);
	if (exp_info->dir == IP_CT_DIR_ORIGINAL) {
		/* Make connection go to the client. */
		newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
		newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
		DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to client)\n",
		       NIPQUAD(newsrcip), NIPQUAD(newdstip));
	} else {
		/* Make the connection go to the server */
		newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
		newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
		DEBUGP("h225_nat_expected: %u.%u.%u.%u->%u.%u.%u.%u (to server)\n",
		       NIPQUAD(newsrcip), NIPQUAD(newdstip));
	}
	port = exp_info->port;
	is_h225 = master_info->is_h225 == H225_PORT;
	UNLOCK_BH(&ip_h323_lock);
	
	if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
		newip = newsrcip;
	else
		newip = newdstip;

	DEBUGP("h225_nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));

	mr.rangesize = 1;
	/* We don't want to manip the per-protocol, just the IPs... */
	mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
	mr.range[0].min_ip = mr.range[0].max_ip = newip;

	/* ... unless we're doing a MANIP_DST, in which case, make
	   sure we map to the correct port */
	if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
		mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
		mr.range[0].min = mr.range[0].max
			= ((union ip_conntrack_manip_proto)
				{ .tcp = { port } });