kern/176268: synproxy not working with route-to
Kajetan Staszkiewicz
kajetan.staszkiewicz at innogames.de
Tue Feb 19 16:00:01 UTC 2013
>Number: 176268
>Category: kern
>Synopsis: synproxy not working with route-to
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Tue Feb 19 16:00:00 UTC 2013
>Closed-Date:
>Last-Modified:
>Originator: Kajetan Staszkiewicz
>Release: 9.1-PRERELEASE
>Organization:
InnoGames GmbH
>Environment:
FreeBSD xxxxxx 9.1-PRERELEASE FreeBSD 9.1-PRERELEASE #1: Mon Feb 18 20:04:57 CET 2013 root at xxxxxx:/usr/obj/usr/src/sys/IGLB3DEBUG amd64
>Description:
When using FreeBSD as a loadbalancer with route-to pf rules redirecting traffic to machines behind the loadbalancer and public addresses assigned on carp interface and it is impossible to use synproxy.
As far as I understand the code, proxied packets to the target server are sent using normal routing table lookup which ends up with sending them on lo0 or carp (the public one, as it has the public address configured) interface.
>How-To-Repeat:
Use synproxy with route-to, outgoing packetes appear on lo0 or carp interfaces instead of the one provided in route-to pf rule.
>Fix:
A known workaround is to provide a second pf rule on lo or carp interface to do synproxy and loadbalancing again. But this solution requires a double (or tripple, when including carp interface) amount of pf rules.
The attached patch modifies the following things:
- Adds an additional route* route_to_ro parameter to pf_send_tcp which is then passed to ip_output.
- Inside pf_test_state_tcp if a synproxy-related packet is about to be sent behind the loadbalancer, create a route structure and fill it with loadbalancing information stored in the state.
The patch has a status of "works for me" and was not yet tested on heavy traffic.
Patch attached with submission follows:
--- sys/contrib/pf/net/pf.c.orig 2013-02-15 18:13:56.000000000 +0100
+++ sys/contrib/pf/net/pf.c 2013-02-19 16:25:25.000000000 +0100
@@ -261,7 +261,8 @@
const struct pf_addr *, const struct pf_addr *,
u_int16_t, u_int16_t, u_int32_t, u_int32_t,
u_int8_t, u_int16_t, u_int16_t, u_int8_t, int,
- u_int16_t, struct ether_header *, struct ifnet *);
+ u_int16_t, struct ether_header *, struct ifnet *,
+ struct route *route_to_ro);
static void pf_send_icmp(struct mbuf *, u_int8_t, u_int8_t,
sa_family_t, struct pf_rule *);
void pf_detach_state(struct pf_state *);
@@ -1570,7 +1571,7 @@
cur->key[PF_SK_WIRE]->port[1],
cur->key[PF_SK_WIRE]->port[0],
cur->src.seqhi, cur->src.seqlo + 1,
- TH_RST|TH_ACK, 0, 0, 0, 1, cur->tag, NULL, NULL);
+ TH_RST|TH_ACK, 0, 0, 0, 1, cur->tag, NULL, NULL, NULL);
}
#ifdef __FreeBSD__
RB_REMOVE(pf_state_tree_id, &V_tree_id, cur);
@@ -2265,7 +2266,7 @@
const struct pf_addr *saddr, const struct pf_addr *daddr,
u_int16_t sport, u_int16_t dport, u_int32_t seq, u_int32_t ack,
u_int8_t flags, u_int16_t win, u_int16_t mss, u_int8_t ttl, int tag,
- u_int16_t rtag, struct ether_header *eh, struct ifnet *ifp)
+ u_int16_t rtag, struct ether_header *eh, struct ifnet *ifp, struct route *route_to_ro)
{
struct mbuf *m;
int len, tlen;
@@ -2442,11 +2443,11 @@
if (eh == NULL) {
#ifdef __FreeBSD__
PF_UNLOCK();
- ip_output(m, (void *)NULL, (void *)NULL, 0,
+ ip_output(m, (void *)NULL, route_to_ro, 0,
(void *)NULL, (void *)NULL);
PF_LOCK();
#else /* ! __FreeBSD__ */
- ip_output(m, (void *)NULL, (void *)NULL, 0,
+ ip_output(m, (void *)NULL, route_to_ro, 0,
(void *)NULL, (void *)NULL);
#endif
} else {
@@ -3681,7 +3682,7 @@
#endif
pd->src, th->th_dport, th->th_sport,
ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0,
- r->return_ttl, 1, 0, pd->eh, kif->pfik_ifp);
+ r->return_ttl, 1, 0, pd->eh, kif->pfik_ifp, NULL);
}
} else if (pd->proto != IPPROTO_ICMP && af == AF_INET &&
r->return_icmp)
@@ -3990,7 +3991,7 @@
pf_send_tcp(r, pd->af, pd->dst, pd->src, th->th_dport,
#endif
th->th_sport, s->src.seqhi, ntohl(th->th_seq) + 1,
- TH_SYN|TH_ACK, 0, s->src.mss, 0, 1, 0, NULL, NULL);
+ TH_SYN|TH_ACK, 0, s->src.mss, 0, 1, 0, NULL, NULL, NULL);
REASON_SET(&reason, PFRES_SYNPROXY);
return (PF_SYNPROXY_DROP);
}
@@ -4445,7 +4446,7 @@
th->th_sport, ntohl(th->th_ack), 0,
TH_RST, 0, 0,
(*state)->rule.ptr->return_ttl, 1, 0,
- pd->eh, kif->pfik_ifp);
+ pd->eh, kif->pfik_ifp, NULL);
src->seqlo = 0;
src->seqhi = 1;
src->max_win = 1;
@@ -4566,6 +4567,12 @@
struct pf_state_peer *src, *dst;
struct pf_state_key *sk;
+ /* A route information is required for route-to and synproxy state combination. */
+ struct route route_to_ro;
+ struct rtentry route_to_rt;
+ struct sockaddr_in route_to_gw;
+ struct route *route_to_ro0 = NULL;
+
key.af = pd->af;
key.proto = IPPROTO_TCP;
if (direction == PF_IN) { /* wire side, straight */
@@ -4614,7 +4621,7 @@
pd->src, th->th_dport, th->th_sport,
(*state)->src.seqhi, ntohl(th->th_seq) + 1,
TH_SYN|TH_ACK, 0, (*state)->src.mss, 0, 1,
- 0, NULL, NULL);
+ 0, NULL, NULL, NULL);
REASON_SET(reason, PFRES_SYNPROXY);
return (PF_SYNPROXY_DROP);
} else if (!(th->th_flags & TH_ACK) ||
@@ -4630,6 +4637,33 @@
(*state)->src.state = PF_TCPS_PROXY_DST;
}
if ((*state)->src.state == PF_TCPS_PROXY_DST) {
+ /* When running a combination of route-to and synproxy state,
+ the SYN packet going to route-to target must use the target interface
+ and gateway stored in connection state instead of standard route table lookup */
+ if ( (*state)->rt_kif ) {
+ /* Assign gateway interface and flags */
+ route_to_rt.rt_flags = RTF_UP|RTF_HOST|RTF_GATEWAY;
+ route_to_rt.rt_ifp = (*state)->rt_kif->pfik_ifp;
+ route_to_rt.rt_ifa = (*state)->rt_kif->pfik_ifp->if_addr;
+ route_to_rt.rt_rmx.rmx_mtu = (*state)->rt_kif->pfik_ifp->if_mtu;
+
+ /* Assign gateway address. */
+ route_to_gw.sin_family = AF_INET;
+ route_to_gw.sin_len = sizeof(struct sockaddr_in);
+ route_to_gw.sin_addr = (*state)->rt_addr.v4;
+
+ /* Assign destination address. */
+ ((struct sockaddr_in*)&route_to_ro.ro_dst)->sin_family = AF_INET;
+ ((struct sockaddr_in*)&route_to_ro.ro_dst)->sin_len = sizeof(struct sockaddr_in);
+ ((struct sockaddr_in*)&route_to_ro.ro_dst)->sin_addr = sk->addr[pd->didx].v4;
+
+ /* Glue things together */
+ route_to_ro.ro_lle = NULL;
+ route_to_rt.rt_gateway = (struct sockaddr*)&route_to_gw;
+ route_to_ro.ro_rt = &route_to_rt;
+ route_to_ro0 = &route_to_ro;
+ }
+
if (direction == (*state)->direction) {
if (((th->th_flags & (TH_SYN|TH_ACK)) != TH_ACK) ||
(ntohl(th->th_ack) != (*state)->src.seqhi + 1) ||
@@ -4648,7 +4682,7 @@
&sk->addr[pd->sidx], &sk->addr[pd->didx],
sk->port[pd->sidx], sk->port[pd->didx],
(*state)->dst.seqhi, 0, TH_SYN, 0,
- (*state)->src.mss, 0, 0, (*state)->tag, NULL, NULL);
+ (*state)->src.mss, 0, 0, (*state)->tag, NULL, NULL, route_to_ro0);
REASON_SET(reason, PFRES_SYNPROXY);
return (PF_SYNPROXY_DROP);
} else if (((th->th_flags & (TH_SYN|TH_ACK)) !=
@@ -4667,7 +4701,7 @@
pd->src, th->th_dport, th->th_sport,
ntohl(th->th_ack), ntohl(th->th_seq) + 1,
TH_ACK, (*state)->src.max_win, 0, 0, 0,
- (*state)->tag, NULL, NULL);
+ (*state)->tag, NULL, NULL, NULL);
#ifdef __FreeBSD__
pf_send_tcp(NULL, (*state)->rule.ptr, pd->af,
#else
@@ -4677,7 +4711,7 @@
sk->port[pd->sidx], sk->port[pd->didx],
(*state)->src.seqhi + 1, (*state)->src.seqlo + 1,
TH_ACK, (*state)->dst.max_win, 0, 0, 1,
- 0, NULL, NULL);
+ 0, NULL, NULL, route_to_ro0);
(*state)->src.seqdiff = (*state)->dst.seqhi -
(*state)->src.seqlo;
(*state)->dst.seqdiff = (*state)->src.seqhi -
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list