From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from gandalf.ozlabs.org (gandalf.ozlabs.org [150.107.74.76]) by passt.top (Postfix) with ESMTPS id A2B5C5A0262 for ; Thu, 9 Mar 2023 03:31:27 +0100 (CET) Received: by gandalf.ozlabs.org (Postfix, from userid 1007) id 4PXCqF5WVSz4xDq; Thu, 9 Mar 2023 13:31:21 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gibson.dropbear.id.au; s=201602; t=1678329081; bh=L9Xf/AbqGRqhiSdAOXrPy76SPKL0P+DwXzibrbEjo+g=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=pLYcGzOUiBh86/8SodXFNgxBoH3H8iGJ9KoASmf5zPQSu4tPhKwclHaMpKFx5kZDL t0m1z8mDh7Xcrz0uyf0dS5PFlwTj8E62u9RKq66hTX/RhUyPzEUnJrdMksg1LGyIkI vl6IA1yDgkCo89PrKYqfZUcTWV65FGpB2zkM00+4= Date: Thu, 9 Mar 2023 13:31:19 +1100 From: David Gibson To: Stefano Brivio Subject: Re: [PATCH v4 2/2] conf, icmp, tcp, udp: Add options to bind to outbound address and interface Message-ID: References: <20230309020947.2609964-1-sbrivio@redhat.com> <20230309020947.2609964-3-sbrivio@redhat.com> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha256; protocol="application/pgp-signature"; boundary="X0nMwe7XubsoO0wa" Content-Disposition: inline In-Reply-To: <20230309020947.2609964-3-sbrivio@redhat.com> Message-ID-Hash: 6O4RQN3EVXLLM5MRYLLJTDE2U6IGTRIV X-Message-ID-Hash: 6O4RQN3EVXLLM5MRYLLJTDE2U6IGTRIV X-MailFrom: dgibson@gandalf.ozlabs.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: passt-dev@passt.top X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: --X0nMwe7XubsoO0wa Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Thu, Mar 09, 2023 at 03:09:47AM +0100, Stefano Brivio wrote: > I didn't notice earlier: libslirp (and slirp4netns) supports binding > outbound sockets to specific IPv4 and IPv6 addresses, to force the > source addresse selection. If we want to claim feature parity, we > should implement that as well. >=20 > Further, Podman supports specifying outbound interfaces as well, but > this is simply done by resolving the primary address for an interface > when the network back-end is started. However, since kernel version > 5.7, commit c427bfec18f2 ("net: core: enable SO_BINDTODEVICE for > non-root users"), we can actually bind to a specific interface name, > which doesn't need to be validated in advance. >=20 > Implement -o / --outbound ADDR to bind to IPv4 and IPv6 addresses, > and --outbound-ip4 and --outbound-ip6 to bind IPv4 and IPv6 sockets > to given interfaces. s/outbound-ip/outbound-if/g > Given that it probably makes little sense to select addresses and > routes from interfaces different than the ones given for outbound > sockets, also assign those as "template" interfaces, by default, > unless explicitly overridden by '-i'. >=20 > For ICMP and UDP, we call sock_l4() to open outbound sockets, as we > already needed to bind to given ports or echo identifiers, and we > can bind() a socket only once: there, pass address (if any) and > interface (if any) for the existing bind() and setsockopt() calls. >=20 > For TCP, in general, we wouldn't otherwise bind sockets. Add a > specific helper to do that. >=20 > For UDP outbound sockets, we need to know if the final destination > of the socket is a loopback address, before we decide whether it > makes sense to bind the socket at all: move the block mangling the > address destination before the creation of the socket in the IPv4 > path. This was already the case for the IPv6 path. >=20 > Signed-off-by: Stefano Brivio Otherwise, Reviewed-by: David Gibson > --- > conf.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++------- > icmp.c | 24 +++++++++++--- > passt.1 | 29 ++++++++++++++-- > passt.h | 10 ++++++ > tcp.c | 60 +++++++++++++++++++++++++++++++++ > udp.c | 54 ++++++++++++++++++++---------- > 6 files changed, 243 insertions(+), 35 deletions(-) >=20 > diff --git a/conf.c b/conf.c > index 3aa3314..e2a7d68 100644 > --- a/conf.c > +++ b/conf.c > @@ -775,7 +775,15 @@ static void usage(const char *name) > info( " -g, --gateway ADDR Pass IPv4 or IPv6 address as gateway"); > info( " default: gateway from interface with default route"); > info( " -i, --interface NAME Interface for addresses and routes"); > - info( " default: interface with first default route"); > + info( " default: from --outbound-if4 and --outbound-if6, if any"); > + info( " otherwise interface with first default route"); > + info( " -o, --outbound ADDR Bind to address as outbound source"); > + info( " can be specified zero to two times (for IPv4 and IPv6)"); > + info( " default: use source address from routing tables"); > + info( " --outbound-if4 NAME Bind to outbound interface for IPv4"); > + info( " default: use interface from default route"); > + info( " --outbound-if6 NAME Bind to outbound interface for IPv6"); > + info( " default: use interface from default route"); > info( " -D, --dns ADDR Use IPv4 or IPv6 address as DNS"); > info( " can be specified multiple times"); > info( " a single, empty option disables DNS information"); > @@ -900,7 +908,7 @@ pasta_opts: > */ > static void conf_print(const struct ctx *c) > { > - char buf4[INET_ADDRSTRLEN], ifn[IFNAMSIZ]; > + char buf4[INET_ADDRSTRLEN], buf6[INET6_ADDRSTRLEN], ifn[IFNAMSIZ]; > int i; > =20 > info("Template interface: %s%s%s%s%s", > @@ -910,6 +918,26 @@ static void conf_print(const struct ctx *c) > c->ifi6 ? if_indextoname(c->ifi6, ifn) : "", > c->ifi6 ? " (IPv6)" : ""); > =20 > + if (*c->ip4.ifname_out || *c->ip6.ifname_out) { > + info("Outbound interface: %s%s%s%s%s", > + *c->ip4.ifname_out ? c->ip4.ifname_out : "", > + *c->ip4.ifname_out ? " (IPv4)" : "", > + (*c->ip4.ifname_out && *c->ip6.ifname_out) ? ", " : "", > + *c->ip6.ifname_out ? c->ip6.ifname_out : "", > + *c->ip6.ifname_out ? " (IPv6)" : ""); > + } > + > + if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) || > + !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out)) { > + info("Outbound address: %s%s%s", > + IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) ? "" : > + inet_ntop(AF_INET, &c->ip4.addr_out, buf4, sizeof(buf4)), > + (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) && > + !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out)) ? ", " : "", > + IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out) ? "" : > + inet_ntop(AF_INET6, &c->ip6.addr_out, buf6, sizeof(buf6))); > + } > + > if (c->mode =3D=3D MODE_PASTA) > info("Namespace interface: %s", c->pasta_ifn); > =20 > @@ -948,8 +976,6 @@ static void conf_print(const struct ctx *c) > } > =20 > if (c->ifi6) { > - char buf6[INET6_ADDRSTRLEN]; > - > if (!c->no_ndp && !c->no_dhcpv6) > info("NDP/DHCPv6:"); > else if (!c->no_ndp) > @@ -1125,6 +1151,7 @@ void conf(struct ctx *c, int argc, char **argv) > {"mac-addr", required_argument, NULL, 'M' }, > {"gateway", required_argument, NULL, 'g' }, > {"interface", required_argument, NULL, 'i' }, > + {"outbound", required_argument, NULL, 'o' }, > {"dns", required_argument, NULL, 'D' }, > {"search", required_argument, NULL, 'S' }, > {"no-tcp", no_argument, &c->no_tcp, 1 }, > @@ -1157,6 +1184,8 @@ void conf(struct ctx *c, int argc, char **argv) > {"runas", required_argument, NULL, 12 }, > {"log-size", required_argument, NULL, 13 }, > {"version", no_argument, NULL, 14 }, > + {"outbound-if4", required_argument, NULL, 15 }, > + {"outbound-if6", required_argument, NULL, 16 }, > { 0 }, > }; > struct get_bound_ports_ns_arg ns_ports_arg =3D { .c =3D c }; > @@ -1166,8 +1195,8 @@ void conf(struct ctx *c, int argc, char **argv) > struct in6_addr *dns6 =3D c->ip6.dns; > struct fqdn *dnss =3D c->dns_search; > struct in_addr *dns4 =3D c->ip4.dns; > + unsigned int ifi4 =3D 0, ifi6 =3D 0; > const char *optstring; > - unsigned int ifi =3D 0; > int name, ret, b, i; > size_t logsize =3D 0; > uid_t uid; > @@ -1175,9 +1204,9 @@ void conf(struct ctx *c, int argc, char **argv) > =20 > if (c->mode =3D=3D MODE_PASTA) { > c->no_dhcp_dns =3D c->no_dhcp_dns_search =3D 1; > - optstring =3D "dqfel:hF:I:p:P:m:a:n:M:g:i:D:S:46t:u:T:U:"; > + optstring =3D "dqfel:hF:I:p:P:m:a:n:M:g:i:o:D:S:46t:u:T:U:"; > } else { > - optstring =3D "dqfel:hs:F:p:P:m:a:n:M:g:i:D:S:461t:u:"; > + optstring =3D "dqfel:hs:F:p:P:m:a:n:M:g:i:o:D:S:461t:u:"; > } > =20 > c->tcp.fwd_in.mode =3D c->tcp.fwd_out.mode =3D 0; > @@ -1295,6 +1324,26 @@ void conf(struct ctx *c, int argc, char **argv) > c->mode =3D=3D MODE_PASST ? "passt " : "pasta "); > fprintf(stdout, VERSION_BLOB); > exit(EXIT_SUCCESS); > + case 15: > + if (*c->ip4.ifname_out) > + die("Redundant outbound interface: %s", optarg); > + > + ret =3D snprintf(c->ip4.ifname_out, > + sizeof(c->ip4.ifname_out), "%s", optarg); > + if (ret <=3D 0 || ret >=3D (int)sizeof(c->ip4.ifname_out)) > + die("Invalid interface name: %s", optarg); > + > + break; > + case 16: > + if (*c->ip6.ifname_out) > + die("Redundant outbound interface: %s", optarg); > + > + ret =3D snprintf(c->ip6.ifname_out, > + sizeof(c->ip6.ifname_out), "%s", optarg); > + if (ret <=3D 0 || ret >=3D (int)sizeof(c->ip6.ifname_out)) > + die("Invalid interface name: %s", optarg); > + > + break; > case 'd': > if (c->debug) > die("Multiple --debug options given"); > @@ -1459,13 +1508,33 @@ void conf(struct ctx *c, int argc, char **argv) > die("Invalid gateway address: %s", optarg); > break; > case 'i': > - if (ifi) > + if (ifi4 || ifi6) > die("Redundant interface: %s", optarg); > =20 > - if (!(ifi =3D if_nametoindex(optarg))) > + if (!(ifi4 =3D ifi6 =3D if_nametoindex(optarg))) > die("Invalid interface name %s: %s", optarg, > strerror(errno)); > break; > + case 'o': > + if (IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out) && > + inet_pton(AF_INET6, optarg, &c->ip6.addr_out) && > + !IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out) && > + !IN6_IS_ADDR_LOOPBACK(&c->ip6.addr_out) && > + !IN6_IS_ADDR_V4MAPPED(&c->ip6.addr_out) && > + !IN6_IS_ADDR_V4COMPAT(&c->ip6.addr_out) && > + !IN6_IS_ADDR_MULTICAST(&c->ip6.addr_out)) > + break; > + > + if (IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) && > + inet_pton(AF_INET, optarg, &c->ip4.addr_out) && > + !IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) && > + !IN4_IS_ADDR_BROADCAST(&c->ip4.addr_out) && > + !IN4_IS_ADDR_MULTICAST(&c->ip4.addr_out)) > + break; > + > + die("Invalid or redundant outbound address: %s", > + optarg); > + break; > case 'D': > if (!strcmp(optarg, "none")) { > if (c->no_dns) > @@ -1557,6 +1626,12 @@ void conf(struct ctx *c, int argc, char **argv) > if (*c->sock_path && c->fd_tap >=3D 0) > die("Options --socket and --fd are mutually exclusive"); > =20 > + if (!ifi4 && *c->ip4.ifname_out) > + ifi4 =3D if_nametoindex(c->ip4.ifname_out); > + > + if (!ifi6 && *c->ip6.ifname_out) > + ifi6 =3D if_nametoindex(c->ip6.ifname_out); > + > conf_ugid(runas, &uid, &gid); > =20 > if (logfile) { > @@ -1566,10 +1641,12 @@ void conf(struct ctx *c, int argc, char **argv) > =20 > nl_sock_init(c, false); > if (!v6_only) > - c->ifi4 =3D conf_ip4(ifi, &c->ip4, c->mac); > + c->ifi4 =3D conf_ip4(ifi4, &c->ip4, c->mac); > if (!v4_only) > - c->ifi6 =3D conf_ip6(ifi, &c->ip6, c->mac); > - if (!c->ifi4 && !c->ifi6) > + c->ifi6 =3D conf_ip6(ifi6, &c->ip6, c->mac); > + if ((!c->ifi4 && !c->ifi6) || > + (*c->ip4.ifname_out && !c->ifi4) || > + (*c->ip6.ifname_out && !c->ifi6)) > die("External interface not usable"); > =20 > /* Inbound port options can be parsed now (after IPv4/IPv6 settings) */ > diff --git a/icmp.c b/icmp.c > index b842fa8..ddf83f8 100644 > --- a/icmp.c > +++ b/icmp.c > @@ -170,8 +170,16 @@ int icmp_tap_handler(const struct ctx *c, int af, co= nst void *addr, > iref.icmp.id =3D id =3D ntohs(ih->un.echo.id); > =20 > if ((s =3D icmp_id_map[V4][id].sock) <=3D 0) { > - s =3D sock_l4(c, AF_INET, IPPROTO_ICMP, NULL, NULL, id, > - iref.u32); > + const struct in_addr *bind_addr =3D NULL; > + const char *bind_if; > + > + bind_if =3D *c->ip4.ifname_out ? c->ip4.ifname_out : NULL; > + > + if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out)) > + bind_addr =3D &c->ip4.addr_out; > + > + s =3D sock_l4(c, AF_INET, IPPROTO_ICMP, bind_addr, > + bind_if, id, iref.u32); > if (s < 0) > goto fail_sock; > if (s > SOCKET_MAX) { > @@ -216,8 +224,16 @@ int icmp_tap_handler(const struct ctx *c, int af, co= nst void *addr, > =20 > iref.icmp.id =3D id =3D ntohs(ih->icmp6_identifier); > if ((s =3D icmp_id_map[V6][id].sock) <=3D 0) { > - s =3D sock_l4(c, AF_INET6, IPPROTO_ICMPV6, NULL, NULL, id, > - iref.u32); > + const struct in6_addr *bind_addr =3D NULL; > + const char *bind_if; > + > + bind_if =3D *c->ip6.ifname_out ? c->ip6.ifname_out : NULL; > + > + if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out)) > + bind_addr =3D &c->ip6.addr_out; > + > + s =3D sock_l4(c, AF_INET6, IPPROTO_ICMPV6, bind_addr, > + bind_if, id, iref.u32); > if (s < 0) > goto fail_sock; > if (s > SOCKET_MAX) { > diff --git a/passt.1 b/passt.1 > index f317c33..6136fa5 100644 > --- a/passt.1 > +++ b/passt.1 > @@ -180,8 +180,33 @@ to allow mapping of local traffic to guest and targe= t namespace. See the > .TP > .BR \-i ", " \-\-interface " " \fIname > Use host interface \fIname\fR to derive addresses and routes. > -Default is to use the interfaces with the first default routes for each = IP > -version. > +Default is to use the interfaces specified by \fB--outbound-if4\fR and > +\fB--outbound-if6\fR, for IPv4 and IPv6 addresses and routes, respective= ly. If > +no interfaces are given, the interface with the first default routes for= each IP > +version is selected. > + > +.TP > +.BR \-o ", " \-\-outbound " " \fIaddr > +Use an IPv4 \fIaddr\fR as source address for IPv4 outbound TCP connectio= ns, UDP > +flows, ICMP requests, or an IPv6 \fIaddr\fR for IPv6 ones, by binding ou= tbound > +sockets to it. > +This option can be specified zero (for defaults) to two times (once for = IPv4, > +once for IPv6). > +By default, the source address is selected by the routing tables. > + > +.TP > +.BR \-\-outbound-if4 " " \fIname > +Bind IPv4 outbound sockets to host interface \fIname\fR, and, unless ano= ther > +interface is specified via \fB-i\fR, \fB--interface\fR, use this interfa= ce to > +derive IPv4 addresses and routes. > +By default, the interface given by the default route is selected. > + > +.TP > +.BR \-\-outbound-if6 " " \fIname > +Bind IPv6 outbound sockets to host interface \fIname\fR, and, unless ano= ther > +interface is specified via \fB-i\fR, \fB--interface\fR, use this interfa= ce to > +derive IPv6 addresses and routes. > +By default, the interface given by the default route is selected. > =20 > .TP > .BR \-D ", " \-\-dns " " \fIaddr > diff --git a/passt.h b/passt.h > index cc60c84..b73f4ff 100644 > --- a/passt.h > +++ b/passt.h > @@ -106,6 +106,8 @@ enum passt_modes { > * @dns: DNS addresses for DHCP, zero-terminated, network order > * @dns_match: Forward DNS query if sent to this address, network order > * @dns_host: Use this DNS on the host for forwarding, network order > + * @addr_out: Optional source address for outbound traffic > + * @ifname_out: Optional interface name to bind outbound sockets to > */ > struct ip4_ctx { > struct in_addr addr; > @@ -115,6 +117,9 @@ struct ip4_ctx { > struct in_addr dns[MAXNS + 1]; > struct in_addr dns_match; > struct in_addr dns_host; > + > + struct in_addr addr_out; > + char ifname_out[IFNAMSIZ]; > }; > =20 > /** > @@ -127,6 +132,8 @@ struct ip4_ctx { > * @dns: DNS addresses for DHCPv6 and NDP, zero-terminated > * @dns_match: Forward DNS query if sent to this address > * @dns_host: Use this DNS on the host for forwarding > + * @addr_out: Optional source address for outbound traffic > + * @ifname_out: Optional interface name to bind outbound sockets to > */ > struct ip6_ctx { > struct in6_addr addr; > @@ -137,6 +144,9 @@ struct ip6_ctx { > struct in6_addr dns[MAXNS + 1]; > struct in6_addr dns_match; > struct in6_addr dns_host; > + > + struct in6_addr addr_out; > + char ifname_out[IFNAMSIZ]; > }; > =20 > #include > diff --git a/tcp.c b/tcp.c > index b674311..8e8d653 100644 > --- a/tcp.c > +++ b/tcp.c > @@ -1946,6 +1946,61 @@ static uint16_t tcp_conn_tap_mss(const struct ctx = *c, > return MIN(mss, USHRT_MAX); > } > =20 > +/** > + * tcp_bind_outbound() - Bind socket to outbound address and interface i= f given > + * @c: Execution context > + * @s: Outbound TCP socket > + * @af: Address family > + */ > +static void tcp_bind_outbound(const struct ctx *c, int s, sa_family_t af) > +{ > + if (af =3D=3D AF_INET) { > + if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out)) { > + struct sockaddr_in addr4 =3D { > + .sin_family =3D AF_INET, > + .sin_port =3D 0, > + .sin_addr =3D c->ip4.addr_out, > + }; > + > + if (bind(s, (struct sockaddr *)&addr4, sizeof(addr4))) { > + debug("Can't bind IPv4 TCP socket address: %s", > + strerror(errno)); > + } > + } > + > + if (*c->ip4.ifname_out) { > + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, > + c->ip4.ifname_out, > + strlen(c->ip4.ifname_out))) { > + debug("Can't bind IPv4 TCP socket to interface:" > + " %s", strerror(errno)); > + } > + } > + } else if (af =3D=3D AF_INET6) { > + if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out)) { > + struct sockaddr_in6 addr6 =3D { > + .sin6_family =3D AF_INET6, > + .sin6_port =3D 0, > + .sin6_addr =3D c->ip6.addr_out, > + }; > + > + if (bind(s, (struct sockaddr *)&addr6, sizeof(addr6))) { > + debug("Can't bind IPv6 TCP socket address: %s", > + strerror(errno)); > + } > + } > + > + if (*c->ip6.ifname_out) { > + if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, > + c->ip6.ifname_out, > + strlen(c->ip6.ifname_out))) { > + debug("Can't bind IPv6 TCP socket to interface:" > + " %s", strerror(errno)); > + } > + } > + } > +} > + > /** > * tcp_conn_from_tap() - Handle connection request (SYN segment) from tap > * @c: Execution context > @@ -2052,6 +2107,11 @@ static void tcp_conn_from_tap(struct ctx *c, int a= f, const void *addr, > if (errno !=3D EADDRNOTAVAIL && errno !=3D EACCES) > conn_flag(c, conn, LOCAL); > =20 > + if ((af =3D=3D AF_INET && !IN4_IS_ADDR_LOOPBACK(&addr4.sin_addr)) || > + (af =3D=3D AF_INET6 && !IN6_IS_ADDR_LOOPBACK(&addr6.sin6_addr) && > + !IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr))) > + tcp_bind_outbound(c, s, af); > + > if (connect(s, sa, sl)) { > if (errno !=3D EINPROGRESS) { > tcp_rst(c, conn); > diff --git a/udp.c b/udp.c > index b7cbfdc..ef486fe 100644 > --- a/udp.c > +++ b/udp.c > @@ -843,20 +843,6 @@ int udp_tap_handler(struct ctx *c, int af, const voi= d *addr, > sa =3D (struct sockaddr *)&s_in; > sl =3D sizeof(s_in); > =20 > - if (!(s =3D udp_tap_map[V4][src].sock)) { > - union udp_epoll_ref uref =3D { .udp.port =3D src }; > - > - s =3D sock_l4(c, AF_INET, IPPROTO_UDP, NULL, NULL, src, > - uref.u32); > - if (s < 0) > - return p->count; > - > - udp_tap_map[V4][src].sock =3D s; > - bitmap_set(udp_act[V4][UDP_ACT_TAP], src); > - } > - > - udp_tap_map[V4][src].ts =3D now->tv_sec; > - > if (IN4_ARE_ADDR_EQUAL(&s_in.sin_addr, &c->ip4.dns_match) && > ntohs(s_in.sin_port) =3D=3D 53) { > s_in.sin_addr =3D c->ip4.dns_host; > @@ -868,13 +854,37 @@ int udp_tap_handler(struct ctx *c, int af, const vo= id *addr, > else > s_in.sin_addr =3D c->ip4.addr_seen; > } > + > + if (!(s =3D udp_tap_map[V4][src].sock)) { > + union udp_epoll_ref uref =3D { .udp.port =3D src }; > + in_addr_t bind_addr =3D { 0 }; > + const char *bind_if =3D NULL; > + > + if (!IN6_IS_ADDR_LOOPBACK(&s_in.sin_addr) && > + *c->ip6.ifname_out) > + bind_if =3D c->ip6.ifname_out; > + > + if (!IN4_IS_ADDR_UNSPECIFIED(&c->ip4.addr_out) && > + !IN4_IS_ADDR_LOOPBACK(&s_in.sin_addr)) > + bind_addr =3D c->ip4.addr_out.s_addr; > + > + s =3D sock_l4(c, AF_INET, IPPROTO_UDP, &bind_addr, > + bind_if, src, uref.u32); > + if (s < 0) > + return p->count; > + > + udp_tap_map[V4][src].sock =3D s; > + bitmap_set(udp_act[V4][UDP_ACT_TAP], src); > + } > + > + udp_tap_map[V4][src].ts =3D now->tv_sec; > } else { > s_in6 =3D (struct sockaddr_in6) { > .sin6_family =3D AF_INET6, > .sin6_port =3D uh->dest, > .sin6_addr =3D *(struct in6_addr *)addr, > }; > - const void *bind_addr =3D &in6addr_any; > + const struct in6_addr *bind_addr =3D &in6addr_any; > =20 > sa =3D (struct sockaddr *)&s_in6; > sl =3D sizeof(s_in6); > @@ -898,9 +908,19 @@ int udp_tap_handler(struct ctx *c, int af, const voi= d *addr, > if (!(s =3D udp_tap_map[V6][src].sock)) { > union udp_epoll_ref uref =3D { .udp.v6 =3D 1, > .udp.port =3D src }; > + const char *bind_if =3D NULL; > + > + if (!IN6_IS_ADDR_LOOPBACK(&s_in6.sin6_addr) && > + *c->ip6.ifname_out) > + bind_if =3D c->ip6.ifname_out; > + > + if (!IN6_IS_ADDR_UNSPECIFIED(&c->ip6.addr_out) && > + !IN6_IS_ADDR_LOOPBACK(&s_in6.sin6_addr) && > + !IN6_IS_ADDR_LINKLOCAL(&s_in6.sin6_addr)) > + bind_addr =3D &c->ip6.addr_out; > =20 > - s =3D sock_l4(c, AF_INET6, IPPROTO_UDP, bind_addr, NULL, > - src, uref.u32); > + s =3D sock_l4(c, AF_INET6, IPPROTO_UDP, bind_addr, > + bind_if, src, uref.u32); > if (s < 0) > return p->count; > =20 --=20 David Gibson | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you. NOT _the_ _other_ | _way_ _around_! http://www.ozlabs.org/~dgibson --X0nMwe7XubsoO0wa Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAEBCAAdFiEEO+dNsU4E3yXUXRK2zQJF27ox2GcFAmQJRPAACgkQzQJF27ox 2GfL1BAAm/rLMm/bpP6Uv0Yzy+zOGNhXWmgAVPfLBXWVFQfX/dZ4g7Slfp+OmwJ0 019ug1RoiS84TdwwC4O9fKDoRdt1BMUlJ1scK43YPmJ2VK0dmi08kYvEkQafD2Qj qe+f9ZT1zR7Dh97eA4E+QuF34hMsr101lSZxI/0WhYiJSx29QaCKjyaiu1UQ/kE7 GbJEJu4FKP/PD0aHZBqLFrvAvcqeQ240Y1WP7wnVdIMBlgP1jHTB6Iao9gUkb9jC k+COKmcKGZR5NrctRwnblLNOyRj8PZWnQXNdvf7oz5j/EM2gG4gKkSnBaHIr3R6g h0+TVRmwzasyQcIIlME8wrlddlg/auobo82kxLfPMcq7DMEaCBzkTnW6t0xXUovM YIUPkWPGGg76fwVJ1Dk3N7Nki5nElha5k9ooN+VzsF/kOOlDQDixG12ZPo8u+29e sVhoPgROMX51Kwvde8kxAUmGLuA0xLKrKVs0SmwoRHpVG1WcxGYeRE8DctK+DXDx 7wHNxaSLpMWBwEohKPHXGatyD2ZK6jrMY8OGEYc7Zff0jQumnc5JRLY+ol6P2cOi uIR+V5Npnzsf4QHJqdAcdBUnpYWWrtvCazl5ZxYmlTN2+cqWMm0Ha5CzQzTAWMJB l3egPMaroRs9fPPhy5SK4YN0jOpshzQEr3MXsvX+trzLVfl7TSA= =5CdM -----END PGP SIGNATURE----- --X0nMwe7XubsoO0wa--