[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openbsd-tech
Subject:    return-icmp-as-dest + bridge: help needed
From:       cedric () berger ! to
Date:       2004-06-30 7:30:16
Message-ID: 200406300730.i5U7UGtV014528 () home ! berger ! to
[Download RAW message or body]

Hi ppl,

That patch introduce a new keyword, "return-icmp-as-dest", that works like
"return-icmp" but makes the returned ICMP packet look like it come from the
destination host, not from the firewall (similar to return-dst).
Using "return-icmp-as-dest" instead of "return-icmp" makes the firewall a bit
more stealthy.

More importantly, this patch make "return-icmp-as-dest" works fine on bridges
too, and complement the work I did a month ago to make "return-rst" work on
pure bridges (bridges with no IP addresses on bridged interfaces).

I'm sending theses patches on the mailing list because we need your help to
test them, PF ppl are a bit overworked right now, and I'm myself soon gonna
slow down OpenBSD work significantly. So if you would like to see theses
features in the upcoming 3.6 release, please test that stuff and tell us what
you tested, what works and what doesn't.

To compile a new system, you basically need to do:
  cd /usr/src
  patch <thisfile
  make includes
  cd /sys/arch/i386/compile/GENERIC
  make depend all install
  cd /usr/src/sbin/pfctl
  make all install
  reboot

Then you can put things like "block return-icmp-as-dest from foo" in your
pf.conf and test it.

Thanks,
Cedric

Index: sys/net/pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.451
diff -u -r1.451 pf.c
--- sys/net/pf.c	10 Jun 2004 14:22:54 -0000	1.451
+++ sys/net/pf.c	21 Jun 2004 15:18:40 -0000
@@ -136,7 +136,12 @@
 			    u_int8_t, u_int16_t, u_int16_t, u_int8_t, int,
 			    struct ether_header *, struct ifnet *);
 void			 pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t,
-			    sa_family_t, struct pf_rule *);
+			    sa_family_t, struct pf_rule *,
+			    struct ether_header *, struct ifnet *, int);
+void			 pf_send_icmp4(struct mbuf *, struct ether_header *,
+			    struct ifnet *, int);
+void			 pf_output4(struct mbuf *, struct mbuf *,
+			    struct ether_header *, struct ifnet *);
 struct pf_rule		*pf_match_translation(struct pf_pdesc *, struct mbuf *,
 			    int, int, struct pfi_kif *,
 			    struct pf_addr *, u_int16_t, struct pf_addr *,
@@ -1389,28 +1394,7 @@
 		h->ip_off = htons(ip_mtudisc ? IP_DF : 0);
 		h->ip_ttl = ttl ? ttl : ip_defttl;
 		h->ip_sum = 0;
-		if (eh == NULL) {
-			ip_output(m, (void *)NULL, (void *)NULL, 0,
-			    (void *)NULL, (void *)NULL);
-		} else {
-			struct route		 ro;
-			struct rtentry		 rt;
-			struct ether_header	*e = (void *)ro.ro_dst.sa_data;
-
-			if (ifp == NULL) {
-				m_freem(m);
-				return;
-			}
-			rt.rt_ifp = ifp;
-			ro.ro_rt = &rt;
-			ro.ro_dst.sa_len = sizeof(ro.ro_dst);
-			ro.ro_dst.sa_family = pseudo_AF_HDRCMPLT;
-			bcopy(eh->ether_dhost, e->ether_shost, ETHER_ADDR_LEN);
-			bcopy(eh->ether_shost, e->ether_dhost, ETHER_ADDR_LEN);
-			e->ether_type = eh->ether_type;
-			ip_output(m, (void *)NULL, &ro, IP_ROUTETOETHER,
-			    (void *)NULL, (void *)NULL);
-		}
+		pf_output4(m, NULL, eh, ifp);
 		break;
 #endif /* INET */
 #ifdef INET6
@@ -1430,10 +1414,10 @@
 
 void
 pf_send_icmp(struct mbuf *m, u_int8_t type, u_int8_t code, sa_family_t af,
-    struct pf_rule *r)
+    struct pf_rule *r, struct ether_header *eh, struct ifnet *ifp, int as_dest)
 {
 	struct m_tag	*mtag;
-	struct mbuf	*m0;
+	struct mbuf	*m0, *m1;
 
 	mtag = m_tag_get(PACKET_TAG_PF_GENERATED, 0, M_NOWAIT);
 	if (mtag == NULL)
@@ -1464,7 +1448,9 @@
 	switch (af) {
 #ifdef INET
 	case AF_INET:
-		icmp_error(m0, type, code, 0, (void *)NULL);
+		m1 = icmp_do_error(m0, type, code, 0, ifp);
+		if (m1 != NULL)
+			pf_send_icmp4(m1, eh, ifp, as_dest);
 		break;
 #endif /* INET */
 #ifdef INET6
@@ -1475,6 +1461,111 @@
 	}
 }
 
+void
+pf_send_icmp4(struct mbuf *m, struct ether_header *eh, struct ifnet *ifp,
+    int as_dest)
+{
+	struct ip		*ip = mtod(m, struct ip *);
+	struct icmp		*icp;
+	struct in_ifaddr	*ia;
+	struct in_addr		 t; 
+	struct mbuf		*opts = NULL;
+	int			 hlen;
+
+	if (eh == NULL && !as_dest) {
+		icmp_reflect(m);
+		return;
+	}
+
+	/* from icmp_reflect code */
+	if (!in_canforward(ip->ip_src) &&
+	    ((ip->ip_src.s_addr & IN_CLASSA_NET) !=
+	    htonl(IN_LOOPBACKNET << IN_CLASSA_NSHIFT))) {
+		m_freem(m);	/* Bad return address */
+		return;
+	}
+	t = ip->ip_dst;
+	ip->ip_dst = ip->ip_src;
+	if (as_dest)
+		goto _skip;
+
+	/*
+	 * If the incoming packet was addressed directly to us,
+	 * use dst as the src for the reply.  Otherwise (broadcast
+	 * or anonymous), use the address which corresponds
+	 * to the incoming interface.
+	 */
+	for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next) {
+		if (t.s_addr == ia->ia_addr.sin_addr.s_addr)
+			break;
+		if ((ia->ia_ifp->if_flags & IFF_BROADCAST) &&
+		    t.s_addr == ia->ia_broadaddr.sin_addr.s_addr)
+			break;
+	}
+	if (ia == NULL && m->m_pkthdr.rcvif != NULL) {
+		struct sockaddr_in icmpdst;
+
+		bzero(&icmpdst, sizeof(icmpdst));
+		icmpdst.sin_len = sizeof(icmpdst);
+		icmpdst.sin_family = AF_INET;
+		icmpdst.sin_addr = t;
+		ia = ifatoia(ifaof_ifpforaddr(sintosa(&icmpdst),
+		    m->m_pkthdr.rcvif));
+	}
+	if (ia == NULL) {
+		m_freem(m);	/* interface has no IP address */
+		return;
+	}
+	t = ia->ia_addr.sin_addr;
+
+_skip:
+	ip->ip_src = t;
+	ip->ip_ttl = MAXTTL;
+
+	opts = icmp_reflect_options(m);
+	m->m_flags &= ~(M_BCAST|M_MCAST);
+
+	/* from icmp_send code */
+	hlen = ip->ip_hl << 2;
+	m->m_data += hlen;
+	m->m_len -= hlen;
+	icp = mtod(m, struct icmp *);
+	icp->icmp_cksum = 0;
+	icp->icmp_cksum = in_cksum(m, ntohs(ip->ip_len) - hlen);
+	m->m_data -= hlen;
+	m->m_len += hlen;
+
+	pf_output4(m, opts, eh, ifp);
+	if (opts)
+		m_free(opts);
+}
+
+void
+pf_output4(struct mbuf *m, struct mbuf *opts, struct ether_header *eh,
+    struct ifnet *ifp)
+{
+	struct route		 ro;
+	struct rtentry		 rt;
+	struct ether_header	*e = (void *)ro.ro_dst.sa_data;
+
+	if (eh == NULL) {
+		ip_output(m, opts, (void *)NULL, 0, (void *)NULL, (void *)NULL);
+		return;
+	}
+	if (ifp == NULL) {
+		m_freem(m);
+		return;
+	}
+	rt.rt_ifp = ifp;
+	ro.ro_rt = &rt;
+	ro.ro_dst.sa_len = sizeof(ro.ro_dst);
+	ro.ro_dst.sa_family = pseudo_AF_HDRCMPLT;
+	bcopy(eh->ether_dhost, e->ether_shost, ETHER_ADDR_LEN);
+	bcopy(eh->ether_shost, e->ether_dhost, ETHER_ADDR_LEN);
+	    e->ether_type = eh->ether_type;
+	ip_output(m, opts, &ro, IP_ROUTETOETHER, (void *)NULL, (void *)NULL);
+}
+
 /*
  * Return 1 if the addresses a and b match (with mask m), otherwise return 0.
  * If n is 0, they match if they are equal. If n is != 0, they match if they
@@ -2630,6 +2721,7 @@
 	if ((r->action == PF_DROP) &&
 	    ((r->rule_flag & PFRULE_RETURNRST) ||
 	    (r->rule_flag & PFRULE_RETURNICMP) ||
+	    (r->rule_flag & PFRULE_RETURNICMPASDEST) ||
 	    (r->rule_flag & PFRULE_RETURN))) {
 		/* undo NAT changes, if they have taken place */
 		if (nr != NULL) {
@@ -2658,10 +2750,12 @@
 			    r->return_ttl, 1, pd->eh, kif->pfik_ifp);
 		} else if ((af == AF_INET) && r->return_icmp)
 			pf_send_icmp(m, r->return_icmp >> 8,
-			    r->return_icmp & 255, af, r);
+			    r->return_icmp & 255, af, r, pd->eh, kif->pfik_ifp,
+			    r->rule_flag & PFRULE_RETURNICMPASDEST);
 		else if ((af == AF_INET6) && r->return_icmp6)
 			pf_send_icmp(m, r->return_icmp6 >> 8,
-			    r->return_icmp6 & 255, af, r);
+			    r->return_icmp6 & 255, af, r, pd->eh, kif->pfik_ifp,
+			    r->rule_flag & PFRULE_RETURNICMPASDEST);
 	}
 
 	if (r->action == PF_DROP)
@@ -2988,6 +3082,7 @@
 
 	if ((r->action == PF_DROP) &&
 	    ((r->rule_flag & PFRULE_RETURNICMP) ||
+	    (r->rule_flag & PFRULE_RETURNICMPASDEST) ||
 	    (r->rule_flag & PFRULE_RETURN))) {
 		/* undo NAT changes, if they have taken place */
 		if (nr != NULL) {
@@ -3003,10 +3098,12 @@
 		}
 		if ((af == AF_INET) && r->return_icmp)
 			pf_send_icmp(m, r->return_icmp >> 8,
-			    r->return_icmp & 255, af, r);
+			    r->return_icmp & 255, af, r, pd->eh, kif->pfik_ifp,
+			    r->rule_flag & PFRULE_RETURNICMPASDEST);
 		else if ((af == AF_INET6) && r->return_icmp6)
 			pf_send_icmp(m, r->return_icmp6 >> 8,
-			    r->return_icmp6 & 255, af, r);
+			    r->return_icmp6 & 255, af, r, pd->eh, kif->pfik_ifp,
+			    r->rule_flag & PFRULE_RETURNICMPASDEST);
 	}
 
 	if (r->action == PF_DROP)
@@ -3529,6 +3626,7 @@
 
 	if ((r->action == PF_DROP) &&
 	    ((r->rule_flag & PFRULE_RETURNICMP) ||
+	    (r->rule_flag & PFRULE_RETURNICMPASDEST) ||
 	    (r->rule_flag & PFRULE_RETURN))) {
 		struct pf_addr *a = NULL;
 
@@ -3555,10 +3653,12 @@
 		}
 		if ((af == AF_INET) && r->return_icmp)
 			pf_send_icmp(m, r->return_icmp >> 8,
-			    r->return_icmp & 255, af, r);
+			    r->return_icmp & 255, af, r, pd->eh, kif->pfik_ifp,
+			    r->rule_flag & PFRULE_RETURNICMPASDEST); 
 		else if ((af == AF_INET6) && r->return_icmp6)
 			pf_send_icmp(m, r->return_icmp6 >> 8,
-			    r->return_icmp6 & 255, af, r);
+			    r->return_icmp6 & 255, af, r, pd->eh, kif->pfik_ifp,
+			    r->rule_flag & PFRULE_RETURNICMPASDEST);
 	}
 
 	if (r->action != PF_PASS)
Index: sys/net/pfvar.h
===================================================================
RCS file: /cvs/src/sys/net/pfvar.h,v
retrieving revision 1.197
diff -u -r1.197 pfvar.h
--- sys/net/pfvar.h	14 Jun 2004 20:53:27 -0000	1.197
+++ sys/net/pfvar.h	21 Jun 2004 15:18:41 -0000
@@ -540,6 +540,7 @@
 #define	PFRULE_NOSYNC		0x0010
 #define PFRULE_SRCTRACK		0x0020  /* track source states */
 #define PFRULE_RULESRCTRACK	0x0040  /* per rule */
+#define PFRULE_RETURNICMPASDEST	0x0080
 
 /* scrub flags */
 #define	PFRULE_NODF		0x0100
Index: sys/netinet/ip_icmp.c
===================================================================
RCS file: /cvs/src/sys/netinet/ip_icmp.c,v
retrieving revision 1.64
diff -u -r1.64 ip_icmp.c
--- sys/netinet/ip_icmp.c	6 Jun 2004 16:49:09 -0000	1.64
+++ sys/netinet/ip_icmp.c	21 Jun 2004 15:18:42 -0000
@@ -615,7 +615,6 @@
 	struct in_ifaddr *ia;
 	struct in_addr t;
 	struct mbuf *opts = 0;
-	int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
 
 	if (!in_canforward(ip->ip_src) &&
 	    ((ip->ip_src.s_addr & IN_CLASSA_NET) !=
@@ -672,6 +671,21 @@
 	ip->ip_src = t;
 	ip->ip_ttl = MAXTTL;
 
+	opts = icmp_reflect_options(m);
+	m->m_flags &= ~(M_BCAST|M_MCAST);
+	icmp_send(m, opts);
+done:
+	if (opts)
+		(void)m_free(opts);
+}
+
+struct mbuf *
+icmp_reflect_options(struct mbuf *m)
+{
+	struct ip *ip = mtod(m, struct ip *);
+	int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
+	struct mbuf *opts = 0;
+
 	if (optlen > 0) {
 		u_char *cp;
 		int opt, cnt;
@@ -744,11 +758,7 @@
 		bcopy((caddr_t)ip + optlen, (caddr_t)(ip + 1),
 		    (unsigned)(m->m_len - sizeof(struct ip)));
 	}
-	m->m_flags &= ~(M_BCAST|M_MCAST);
-	icmp_send(m, opts);
-done:
-	if (opts)
-		(void)m_free(opts);
+	return (opts);
 }
 
 /*
Index: sys/netinet/ip_icmp.h
===================================================================
RCS file: /cvs/src/sys/netinet/ip_icmp.h,v
retrieving revision 1.20
diff -u -r1.20 ip_icmp.h
--- sys/netinet/ip_icmp.h	2 Jun 2003 23:28:14 -0000	1.20
+++ sys/netinet/ip_icmp.h	21 Jun 2004 15:18:42 -0000
@@ -211,6 +211,8 @@
 void	icmp_input(struct mbuf *, ...);
 void	icmp_init(void);
 void	icmp_reflect(struct mbuf *);
+struct mbuf *
+	icmp_reflect_options(struct mbuf *);
 void	icmp_send(struct mbuf *, struct mbuf *);
 int	icmp_sysctl(int *, u_int, void *, size_t *, void *, size_t);
 struct rtentry *
Index: share/man/man5/pf.conf.5
===================================================================
RCS file: /cvs/src/share/man/man5/pf.conf.5,v
retrieving revision 1.297
diff -u -r1.297 pf.conf.5
--- share/man/man5/pf.conf.5	9 May 2004 10:51:55 -0000	1.297
+++ share/man/man5/pf.conf.5	21 Jun 2004 15:18:45 -0000
@@ -1110,6 +1110,13 @@
 This causes ICMP messages to be returned for packets which match the rule.
 By default this is an ICMP UNREACHABLE message, however this
 can be overridden by specifying a message as a code or number.
+.It Ar return-icmp-as-dest
+This does the same as
+.Ar return-icmp ,
+but makes the ICMP packet appear to come from the destination host.
+Unlike
+.Ar return-icmp ,
+this will work on pure bridge.
 .It Ar return
 This causes a TCP RST to be returned for
 .Xr tcp 4
@@ -2524,6 +2531,8 @@
 return         = "drop" | "return" | "return-rst" [ "( ttl" number ")" ] |
                  "return-icmp" [ "(" icmpcode ["," icmp6code ] ")" ] |
                  "return-icmp6" [ "(" icmp6code ")" ]
+		 "return-icmp-as-dest" [ "(" icmpcode ")" ] |
+
 icmpcode       = ( icmp-code-name | icmp-code-number )
 icmp6code      = ( icmp6-code-name | icmp6-code-number )
 
Index: sbin/pfctl/parse.y
===================================================================
RCS file: /cvs/src/sbin/pfctl/parse.y,v
retrieving revision 1.455
diff -u -r1.455 parse.y
--- sbin/pfctl/parse.y	10 Jun 2004 14:22:54 -0000	1.455
+++ sbin/pfctl/parse.y	21 Jun 2004 15:18:49 -0000
@@ -387,7 +387,7 @@
 %token	REASSEMBLE FRAGDROP FRAGCROP ANCHOR NATANCHOR RDRANCHOR BINATANCHOR
 %token	SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY RANDOMID
 %token	REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG HOSTID
-%token	ANTISPOOF FOR
+%token	ANTISPOOF FOR RETURNICMPASDEST RETURNICMP6ASDEST
 %token	BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY
 %token	ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT
 %token	QUEUE PRIORITY QLIMIT
@@ -408,6 +408,7 @@
 %type	<v.icmp>		icmpspec
 %type	<v.icmp>		icmp_list icmp_item
 %type	<v.icmp>		icmp6_list icmp6_item
+%type	<v.i>			returnicmp returnicmp6
 %type	<v.fromto>		fromto
 %type	<v.peer>		ipportspec from to
 %type	<v.host>		ipspec xhost host dynaddr host_list
@@ -1476,6 +1477,11 @@
 				r.return_icmp = $1.w;
 				r.return_icmp6 = $1.w2;
 				break;
+			case PFRULE_RETURNICMPASDEST:
+				r.rule_flag |= PFRULE_RETURNICMPASDEST;
+				r.return_icmp = $1.w;
+				r.return_icmp6 = $1.w2;
+				break;
 			case PFRULE_RETURN:
 				r.rule_flag |= PFRULE_RETURN;
 				r.return_icmp = $1.w;
@@ -1849,17 +1855,17 @@
 			$$.w = $4;
 			$$.w2 = 0;
 		}
-		| RETURNICMP		{
-			$$.b2 = PFRULE_RETURNICMP;
+		| returnicmp		{
+			$$.b2 = $1;
 			$$.w = returnicmpdefault;
 			$$.w2 = returnicmp6default;
 		}
-		| RETURNICMP6		{
-			$$.b2 = PFRULE_RETURNICMP;
+		| returnicmp6		{
+			$$.b2 = $1;
 			$$.w = returnicmpdefault;
 			$$.w2 = returnicmp6default;
 		}
-		| RETURNICMP '(' STRING ')'	{
+		| returnicmp '(' STRING ')'	{
 			$$.b2 = PFRULE_RETURNICMP;
 			if (!($$.w = parseicmpspec($3, AF_INET))) {
 				free($3);
@@ -1868,7 +1874,7 @@
 			free($3);
 			$$.w2 = returnicmp6default;
 		}
-		| RETURNICMP6 '(' STRING ')'	{
+		| returnicmp6 '(' STRING ')'	{
 			$$.b2 = PFRULE_RETURNICMP;
 			$$.w = returnicmpdefault;
 			if (!($$.w2 = parseicmpspec($3, AF_INET6))) {
@@ -1877,7 +1883,7 @@
 			}
 			free($3);
 		}
-		| RETURNICMP '(' STRING comma STRING ')' {
+		| returnicmp '(' STRING comma STRING ')' {
 			$$.b2 = PFRULE_RETURNICMP;
 			if (!($$.w = parseicmpspec($3, AF_INET)) ||
 			    !($$.w2 = parseicmpspec($5, AF_INET6))) {
@@ -1895,6 +1901,14 @@
 		}
 		;
 
+returnicmp	: RETURNICMP		{ $$ = PFRULE_RETURNICMP; }
+		| RETURNICMPASDEST	{ $$ = PFRULE_RETURNICMPASDEST; }
+		;
+
+returnicmp6	: RETURNICMP6		{ $$ = PFRULE_RETURNICMP; }
+		| RETURNICMP6ASDEST	{ $$ = PFRULE_RETURNICMPASDEST; }
+		;
+
 dir		: /* empty */			{ $$ = 0; }
 		| IN				{ $$ = PF_IN; }
 		| OUT				{ $$ = PF_OUT; }
@@ -4409,7 +4423,10 @@
 		{ "require-order",	REQUIREORDER},
 		{ "return",		RETURN},
 		{ "return-icmp",	RETURNICMP},
+		{ "return-icmp-as-dest",RETURNICMPASDEST},
 		{ "return-icmp6",	RETURNICMP6},
+		{ "return-icmp6-as-dest",
+					RETURNICMP6ASDEST},
 		{ "return-rst",		RETURNRST},
 		{ "round-robin",	ROUNDROBIN},
 		{ "route-to",		ROUTETO},
Index: sbin/pfctl/pfctl_parser.c
===================================================================
RCS file: /cvs/src/sbin/pfctl/pfctl_parser.c,v
retrieving revision 1.201
diff -u -r1.201 pfctl_parser.c
--- sbin/pfctl/pfctl_parser.c	10 Jun 2004 14:22:54 -0000	1.201
+++ sbin/pfctl/pfctl_parser.c	21 Jun 2004 15:18:50 -0000
@@ -654,7 +654,8 @@
 				printf(" return-rst");
 			else
 				printf(" return-rst(ttl %d)", r->return_ttl);
-		} else if (r->rule_flag & PFRULE_RETURNICMP) {
+		} else if (r->rule_flag & (PFRULE_RETURNICMP|
+		    PFRULE_RETURNICMPASDEST)) {
 			const struct icmpcodeent	*ic, *ic6;
 
 			ic = geticmpcodebynumber(r->return_icmp >> 8,
@@ -664,21 +665,30 @@
 
 			switch (r->af) {
 			case AF_INET:
-				printf(" return-icmp");
+				if (r->rule_flag & PFRULE_RETURNICMPASDEST)
+					printf(" return-icmp-as-dest");
+				else
+					printf(" return-icmp");
 				if (ic == NULL)
 					printf("(%u)", r->return_icmp & 255);
 				else
 					printf("(%s)", ic->name);
 				break;
 			case AF_INET6:
-				printf(" return-icmp6");
+				if (r->rule_flag & PFRULE_RETURNICMPASDEST)
+					printf(" return-icmp6-as-dest");
+				else
+					printf(" return-icmp6");
 				if (ic6 == NULL)
 					printf("(%u)", r->return_icmp6 & 255);
 				else
 					printf("(%s)", ic6->name);
 				break;
 			default:
-				printf(" return-icmp");
+				if (r->rule_flag & PFRULE_RETURNICMPASDEST)
+					printf(" return-icmp-as-dest");
+				else
+					printf(" return-icmp");
 				if (ic == NULL)
 					printf("(%u, ", r->return_icmp & 255);
 				else

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic