For now when we forward a ping to the host we leave the host side forwarding address and port blank since we don't necessarily know what source address and id will be used by the kernel. When the outbound address option is active, though, we do know the address at least, so we can record it in the flowside. Having done that, use it as the primary source of truth, binding the outgoing socket based on the information in there. This allows the possibility of more complex rules for what outbound address and/or id we use in future. To implement this we create a new helper which sets up a new socket based on information in a flowside, which will also have future uses. It behaves slightly differently from the existing ICMP code, in that it doesn't bind to a specific interface if given a loopback address. This is logically correct - the loopback address means we need to operate through the host's loopback interface, not ifname_out. We didn't need it in ICMP because ICMP will never generate a loopback address at this point, however we intend to change that in future. Signed-off-by: David Gibson <david(a)gibson.dropbear.id.au> --- flow.c | 32 ++++++++++++++++++++++++++++++++ flow.h | 3 +++ icmp.c | 23 ++++++++++------------- util.c | 6 +++--- util.h | 3 +++ 5 files changed, 51 insertions(+), 16 deletions(-) diff --git a/flow.c b/flow.c index c94351d7..3770af37 100644 --- a/flow.c +++ b/flow.c @@ -143,6 +143,38 @@ static void flowside_from_af(struct flowside *fside, sa_family_t af, fside->eport = eport; } +/** flowside_sock_l4() - Create and bind socket based on flowside + * @c: Execution context + * @proto: Protocol number + * @pif: Interface for this socket + * @tgt: Target flowside + * @data: epoll reference portion for protocol handlers + * + * Return: socket fd of protocol @proto bound to the forwarding address and port + * from @tgt (if specified). + */ +int flowside_sock_l4(const struct ctx *c, uint8_t proto, uint8_t pif, + const struct flowside *tgt, uint32_t data) +{ + const char *ifname = NULL; + union sockaddr_inany sa; + socklen_t sl; + + ASSERT(pif == PIF_HOST); /* TODO: support other pifs */ + + sockaddr_from_inany(&sa, &sl, &tgt->faddr, tgt->fport, c->ifi6); + + if (inany_is_loopback(&tgt->faddr)) + ifname = NULL; + else if (sa.sa_family == AF_INET) + ifname = c->ip4.ifname_out; + else if (sa.sa_family == AF_INET6) + ifname = c->ip6.ifname_out; + + return sock_l4_sa(c, proto, &sa, sl, ifname, sa.sa_family == AF_INET6, + data); +} + /** flow_log_ - Log flow-related message * @f: flow the message is related to * @pri: Log priority diff --git a/flow.h b/flow.h index 90389a5e..948f2ea9 100644 --- a/flow.h +++ b/flow.h @@ -164,6 +164,9 @@ static inline bool flowside_eq(const struct flowside *left, left->fport == right->fport; } +int flowside_sock_l4(const struct ctx *c, uint8_t proto, uint8_t pif, + const struct flowside *tgt, uint32_t data); + /** * struct flow_common - Common fields for packet flows * @state: State of the flow table entry diff --git a/icmp.c b/icmp.c index b297b9ac..cb3278e9 100644 --- a/icmp.c +++ b/icmp.c @@ -157,30 +157,27 @@ static struct icmp_ping_flow *icmp_ping_new(const struct ctx *c, union epoll_ref ref = { .type = EPOLL_TYPE_PING }; union flow *flow = flow_alloc(); struct icmp_ping_flow *pingf; + const struct flowside *tgt; const void *bind_addr; - const char *bind_if; if (!flow) return NULL; flow_initiate_af(flow, PIF_TAP, af, saddr, id, daddr, id); - /* FIXME: Record outbound source address when known */ - flow_target_af(flow, PIF_HOST, af, NULL, 0, daddr, 0); - pingf = FLOW_SET_TYPE(flow, flowtype, ping); - - pingf->seq = -1; - if (af == AF_INET) { + if (af == AF_INET) bind_addr = &c->ip4.addr_out; - bind_if = c->ip4.ifname_out; - } else { + else if (af == AF_INET6) bind_addr = &c->ip6.addr_out; - bind_if = c->ip6.ifname_out; - } + + tgt = flow_target_af(flow, PIF_HOST, af, bind_addr, 0, daddr, 0); + pingf = FLOW_SET_TYPE(flow, flowtype, ping); + + pingf->seq = -1; ref.flowside = FLOW_SIDX(flow, TGTSIDE); - pingf->sock = sock_l4(c, af, flow_proto[flowtype], bind_addr, bind_if, - 0, ref.data); + pingf->sock = flowside_sock_l4(c, flow_proto[flowtype], PIF_HOST, + tgt, ref.data); if (pingf->sock < 0) { warn("Cannot open \"ping\" socket. You might need to:"); diff --git a/util.c b/util.c index 4e3d84a1..9ba9908d 100644 --- a/util.c +++ b/util.c @@ -44,9 +44,9 @@ * * Return: newly created socket, negative error code on failure */ -static int sock_l4_sa(const struct ctx *c, uint8_t proto, - const void *sa, socklen_t sl, - const char *ifname, bool v6only, uint32_t data) +int sock_l4_sa(const struct ctx *c, uint8_t proto, + const void *sa, socklen_t sl, + const char *ifname, bool v6only, uint32_t data) { sa_family_t af = ((const struct sockaddr *)sa)->sa_family; union epoll_ref ref = { .data = data }; diff --git a/util.h b/util.h index eebb027b..bbf10778 100644 --- a/util.h +++ b/util.h @@ -143,6 +143,9 @@ struct ctx; /* cppcheck-suppress funcArgNamesDifferent */ __attribute__ ((weak)) int ffsl(long int i) { return __builtin_ffsl(i); } +int sock_l4_sa(const struct ctx *c, uint8_t proto, + const void *sa, socklen_t sl, + const char *ifname, bool v6only, uint32_t data); int sock_l4(const struct ctx *c, sa_family_t af, uint8_t proto, const void *bind_addr, const char *ifname, uint16_t port, uint32_t data); -- 2.45.2