>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@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:
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "[email protected]"