[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