From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=none (p=none dis=none) header.from=gibson.dropbear.id.au Authentication-Results: passt.top; dkim=pass (2048-bit key; secure) header.d=gibson.dropbear.id.au header.i=@gibson.dropbear.id.au header.a=rsa-sha256 header.s=202602 header.b=B7qC3hGc; dkim-atps=neutral Received: from mail.ozlabs.org (gandalf.ozlabs.org [150.107.74.76]) by passt.top (Postfix) with ESMTPS id 079BA5A0262 for ; Tue, 10 Mar 2026 05:04:32 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gibson.dropbear.id.au; s=202602; t=1773115469; bh=B2KMNFrJzyC9QGjpZQT+zUivn1BGS05rHPAkhUUaUMQ=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From; b=B7qC3hGcE2jC+JsqSFbOln0MTUQenNa+0xWM+q+BGR9VMkW2OYxzZn3bXZLrJR8xd 6z3/LMArsBkO7+O+wRlCtc8d/5LSakfUOceDIYy93YKUd8vOYBPFT7/t3YfAfWVTuU d0soNJsGU4k7nM2SosLffl+kv6rKFnYKBKHV/CZb1hAW1LGDcXfMUNouhx28W+6gvo df+sK8VKeD8ktG5Lz+NW00SN463dVB0l9TjyvTlHg70B1T27dJEHh8eInzGefaFqOs uKKU5qcT0zVmTkkq7BDWM1YglKsJPd3cYyZjY2hugQH69qiT323b/6mx6ov7nke277 LHF8je/aiDFPw== Received: by gandalf.ozlabs.org (Postfix, from userid 1007) id 4fVKyP3Bk7z4wHX; Tue, 10 Mar 2026 15:04:29 +1100 (AEDT) Date: Tue, 10 Mar 2026 15:04:24 +1100 From: David Gibson To: Jon Maloy Subject: Re: [PATCH v5 13/13] netlink: Add host-side monitoring for late template interface binding Message-ID: References: <20260222174445.743845-1-jmaloy@redhat.com> <20260222174445.743845-14-jmaloy@redhat.com> MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="5ZjgxpLekwo1ZaS6" Content-Disposition: inline In-Reply-To: <20260222174445.743845-14-jmaloy@redhat.com> Message-ID-Hash: FFUFTUNDZWL5TTSMNCBN455SQRH4D5KK X-Message-ID-Hash: FFUFTUNDZWL5TTSMNCBN455SQRH4D5KK 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: sbrivio@redhat.com, dgibson@redhat.com, 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: --5ZjgxpLekwo1ZaS6 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Sun, Feb 22, 2026 at 12:44:45PM -0500, Jon Maloy wrote: > When pasta starts without an active template interface (e.g., WiFi > not yet connected), it falls back to local mode. This change adds > support for late binding: when the template interface gets an address > or a default route later, pasta detects this via a host-side netlink > socket and propagates the configuration to the namespace. >=20 > Late binding occurs when: > - A specific interface is given via -I and later gets an address/route. > - No interface is specified, and any interface gets an address/route. > In the latter case the first discovered interface is adopted as > template. >=20 > In this commit we add a host-side netlink socket to monitor link, > address, and route changes on the template interface. We add a > corresponding nl_linkaddr_host_handler() function to process such > events and propagate the changes to the namespace. >=20 > Signed-off-by: Jon Maloy This is a big one, since you're already respinning based on my earlier comments, I'm going to defer review until the next spin. > --- > epoll_type.h | 2 + > isolation.c | 5 + > netlink.c | 397 ++++++++++++++++++++++++++++++++++++++++++++++++++- > netlink.h | 3 + > passt.c | 5 + > 5 files changed, 407 insertions(+), 5 deletions(-) >=20 > diff --git a/epoll_type.h b/epoll_type.h > index a90ffb6..cd17a64 100644 > --- a/epoll_type.h > +++ b/epoll_type.h > @@ -46,6 +46,8 @@ enum epoll_type { > EPOLL_TYPE_REPAIR, > /* Netlink neighbour subscription socket */ > EPOLL_TYPE_NL_NEIGH, > + /* Netlink link/address subscription socket for late binding */ > + EPOLL_TYPE_NL_LINKADDR_HOST, > =20 > EPOLL_NUM_TYPES, > }; > diff --git a/isolation.c b/isolation.c > index b25f349..8087563 100644 > --- a/isolation.c > +++ b/isolation.c > @@ -356,6 +356,11 @@ int isolate_prefork(const struct ctx *c) > if (c->mode =3D=3D MODE_PASTA) { > /* Keep CAP_SYS_ADMIN, so we can enter the netns */ > ns_caps |=3D BIT(CAP_SYS_ADMIN); > + /* Keep CAP_NET_ADMIN for dynamic interface configuration, > + * so we can propagate addresses and routes when template > + * interface comes up after start > + */ > + ns_caps |=3D BIT(CAP_NET_ADMIN); > /* Keep CAP_NET_BIND_SERVICE, so we can splice > * outbound connections to low port numbers > */ > diff --git a/netlink.c b/netlink.c > index 769cb23..a1790a4 100644 > --- a/netlink.c > +++ b/netlink.c > @@ -37,6 +37,14 @@ > #include "ip.h" > #include "netlink.h" > #include "epoll_ctl.h" > +#include "conf.h" > +#include "arp.h" > +#include "ndp.h" > +#include "tap.h" > +#include "fwd.h" > + > +/* Default namespace interface name */ > +extern const char *pasta_default_ifn; > =20 > /* Same as RTA_NEXT() but for nexthops: RTNH_NEXT() doesn't take 'attrle= n' */ > #define RTNH_NEXT_AND_DEC(rtnh, attrlen) \ > @@ -56,10 +64,14 @@ > #define NLBUFSIZ 65536 > =20 > /* Socket in init, in target namespace, sequence (just needs to be monot= onic) */ > -int nl_sock =3D -1; > -int nl_sock_ns =3D -1; > -static int nl_sock_neigh =3D -1; > -static int nl_seq =3D 1; > +int nl_sock =3D -1; > +int nl_sock_ns =3D -1; > +static int nl_sock_neigh =3D -1; > +static int nl_sock_linkaddr_host =3D -1; > +static int nl_seq =3D 1; > + > +static int nl_addr_del(int s, unsigned int ifi, sa_family_t af, > + const void *addr, int prefix_len); > =20 > /** > * nl_sock_init_do() - Set up netlink sockets in init or target namespace > @@ -91,6 +103,329 @@ static int nl_sock_init_do(void *arg) > return 0; > } > =20 > +/** > + * nl_linkaddr_host_msg_read() - Handle host-side link/addr/route changes > + * @c: Execution context > + * @nh: Netlink message header > + * > + * Monitor template interface changes and propagate to namespace. > + * Supports late binding: if no template was detected at startup, > + * adopt the interface specified by -I when it gets an address. > + */ > +static void nl_linkaddr_host_msg_read(struct ctx *c, const struct nlmsgh= dr *nh) > +{ > + if (nh->nlmsg_type =3D=3D NLMSG_DONE || nh->nlmsg_type =3D=3D NLMSG_ERR= OR) > + return; > + > + if (nh->nlmsg_type =3D=3D RTM_NEWADDR || nh->nlmsg_type =3D=3D RTM_DELA= DDR) { > + bool is_new =3D (nh->nlmsg_type =3D=3D RTM_NEWADDR); > + const struct ifaddrmsg *ifa =3D NLMSG_DATA(nh); > + char buf[INET6_ADDRSTRLEN]; > + unsigned int template_ifi; > + union inany_addr inany; > + char ifname[IFNAMSIZ]; > + struct rtattr *rta; > + void *addr =3D NULL; > + bool is_default; > + sa_family_t af; > + int prefix_len; > + bool is_match; > + bool unbound; > + size_t na; > + int rc; > + > + if (!if_indextoname(ifa->ifa_index, ifname)) > + snprintf(ifname, sizeof(ifname), "?"); > + > + /* Get template interface index */ > + if (ifa->ifa_family =3D=3D AF_INET) > + template_ifi =3D c->ifi4; > + else if (ifa->ifa_family =3D=3D AF_INET6) > + template_ifi =3D c->ifi6; > + else > + return; > + > + /* Check for late binding conditions */ > + is_default =3D !strcmp(c->pasta_ifn, pasta_default_ifn); > + is_match =3D !strcmp(ifname, c->pasta_ifn); > + unbound =3D (ifa->ifa_family =3D=3D AF_INET) ? > + c->ifi4 <=3D 0 : c->ifi6 <=3D 0; > + > + if (unbound && (is_default || is_match)) { > + debug("Late binding: using %s as %s template", ifname, > + ifa->ifa_family =3D=3D AF_INET ? "IPv4" : "IPv6"); > + > + if (ifa->ifa_family =3D=3D AF_INET) { > + c->ifi4 =3D ifa->ifa_index; > + template_ifi =3D c->ifi4; > + } else { > + c->ifi6 =3D ifa->ifa_index; > + template_ifi =3D c->ifi6; > + } > + > + if (is_default) > + snprintf(c->pasta_ifn, sizeof(c->pasta_ifn), > + "%s", ifname); > + } > + > + if (ifa->ifa_index !=3D template_ifi) > + return; > + > + rta =3D IFA_RTA(ifa); > + na =3D IFA_PAYLOAD(nh); > + > + for (; RTA_OK(rta, na); rta =3D RTA_NEXT(rta, na)) { > + if (ifa->ifa_family =3D=3D AF_INET && > + rta->rta_type =3D=3D IFA_LOCAL) { > + addr =3D RTA_DATA(rta); > + break; > + } else if (ifa->ifa_family =3D=3D AF_INET6 && > + rta->rta_type =3D=3D IFA_ADDRESS) { > + addr =3D RTA_DATA(rta); > + break; > + } > + } > + > + if (!addr) { > + info("No addr found in netlink linkaddr message"); > + return; > + } > + > + af =3D ifa->ifa_family; > + inany_from_af(&inany, af, addr); > + inet_ntop(af, addr, buf, sizeof(buf)); > + > + /* IPv4 prefix stored as IPv4-mapped, so add 96 bits */ > + prefix_len =3D ifa->ifa_prefixlen + (af =3D=3D AF_INET ? 96 : 0); > + > + if (!is_new) { > + fwd_remove_addr(c, &inany); > + nl_addr_del(nl_sock_ns, c->pasta_ifi, > + af, addr, ifa->ifa_prefixlen); > + return; > + } > + > + rc =3D nl_addr_set(nl_sock_ns, c->pasta_ifi, > + af, addr, ifa->ifa_prefixlen); > + if (rc < 0) { > + debug("Failed to add %s/%u to ns: %s", > + buf, ifa->ifa_prefixlen, strerror_(-rc)); > + } else { > + fwd_set_addr(c, &inany, > + CONF_ADDR_HOST | CONF_ADDR_OBSERVED, > + prefix_len); > + debug("Added %s/%u to namespace", > + buf, ifa->ifa_prefixlen); > + } > + return; > + } > + > + if (nh->nlmsg_type =3D=3D RTM_NEWROUTE || nh->nlmsg_type =3D=3D RTM_DEL= ROUTE) { > + bool is_new =3D (nh->nlmsg_type =3D=3D RTM_NEWROUTE); > + const struct rtmsg *rtm =3D NLMSG_DATA(nh); > + struct rtattr *rta =3D RTM_RTA(rtm); > + size_t na =3D RTM_PAYLOAD(nh); > + unsigned int template_ifi; > + char ifname[IFNAMSIZ]; > + unsigned int oif =3D 0; > + void *gw =3D NULL; > + bool is_default; > + bool is_match; > + bool unbound; > + > + /* We are only interested in default routes */ > + if (rtm->rtm_dst_len !=3D 0) > + return; > + > + for (; RTA_OK(rta, na); rta =3D RTA_NEXT(rta, na)) { > + if (rta->rta_type =3D=3D RTA_GATEWAY) > + gw =3D RTA_DATA(rta); > + else if (rta->rta_type =3D=3D RTA_OIF) > + oif =3D *(unsigned int *)RTA_DATA(rta); > + } > + > + if (!gw || !oif) > + return; > + > + /* Get interface name for late binding check */ > + if (!if_indextoname(oif, ifname)) > + return; > + > + /* Check for late binding conditions */ > + is_default =3D !strcmp(c->pasta_ifn, pasta_default_ifn); > + is_match =3D !strcmp(ifname, c->pasta_ifn); > + > + if (rtm->rtm_family =3D=3D AF_INET) > + template_ifi =3D c->ifi4; > + else if (rtm->rtm_family =3D=3D AF_INET6) > + template_ifi =3D c->ifi6; > + else > + return; > + > + unbound =3D (rtm->rtm_family =3D=3D AF_INET) ? > + c->ifi4 <=3D 0 : c->ifi6 <=3D 0; > + > + if (unbound && (is_default || is_match)) { > + debug("Late binding (route): using %s as %s template", > + ifname, > + rtm->rtm_family =3D=3D AF_INET ? "IPv4" : "IPv6"); > + > + if (rtm->rtm_family =3D=3D AF_INET) { > + c->ifi4 =3D oif; > + template_ifi =3D c->ifi4; > + } else { > + c->ifi6 =3D oif; > + template_ifi =3D c->ifi6; > + } > + > + if (is_default) > + snprintf(c->pasta_ifn, sizeof(c->pasta_ifn), > + "%s", ifname); > + } > + > + if (oif !=3D template_ifi) > + return; > + > + if (rtm->rtm_family =3D=3D AF_INET) { > + char buf[INET_ADDRSTRLEN]; > + > + if (!is_new) { > + c->ip4.guest_gw =3D (struct in_addr){ 0 }; > + c->ip4.our_tap_addr =3D (struct in_addr){ 0 }; > + return; > + } > + c->ip4.guest_gw =3D *(struct in_addr *)gw; > + c->ip4.our_tap_addr =3D c->ip4.guest_gw; > + nl_route_set_def(nl_sock_ns, c->pasta_ifi, AF_INET, gw); > + inet_ntop(AF_INET, &c->ip4.guest_gw, buf, sizeof(buf)); > + debug("Set IPv4 default route via %s", buf); > + } else if (rtm->rtm_family =3D=3D AF_INET6) { > + char buf[INET6_ADDRSTRLEN]; > + > + if (!is_new) { > + c->ip6.guest_gw =3D (struct in6_addr){ 0 }; > + return; > + } > + c->ip6.guest_gw =3D *(struct in6_addr *)gw; > + nl_route_set_def(nl_sock_ns, c->pasta_ifi, > + AF_INET6, gw); > + inet_ntop(AF_INET6, &c->ip6.guest_gw, buf, sizeof(buf)); > + debug("Set IPv6 default route via %s", buf); > + } > + } > +} > + > +/** > + * nl_linkaddr_host_handler() - Handle events from host link/addr notifi= er > + * @c: Execution context > + * > + * Monitor template interface changes and propagate to namespace > + */ > +void nl_linkaddr_host_handler(struct ctx *c) > +{ > + char buf[NLBUFSIZ]; > + > + for (;;) { > + ssize_t n =3D recv(nl_sock_linkaddr_host, buf, sizeof(buf), > + MSG_DONTWAIT); > + struct nlmsghdr *nh =3D (struct nlmsghdr *)buf; > + > + if (n < 0) { > + if (errno =3D=3D EINTR) > + continue; > + if (errno !=3D EAGAIN) > + info("Host recv() error: %s", strerror_(errno)); > + break; > + } > + > + info("Host netlink: received %zd bytes", n); > + > + for (; NLMSG_OK(nh, n); nh =3D NLMSG_NEXT(nh, n)) > + nl_linkaddr_host_msg_read(c, nh); > + } > +} > + > +/** > + * nl_linkaddr_host_init_do() - Create host-side link/addr notifier sock= et > + * @arg: Unused > + * > + * Return: 0 on success, -1 on failure > + */ > +static int nl_linkaddr_host_init_do(void *arg) > +{ > + struct sockaddr_nl addr =3D { > + .nl_family =3D AF_NETLINK, > + .nl_groups =3D RTMGRP_LINK | > + RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | > + RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE, > + }; > + > + (void)arg; > + > + nl_sock_linkaddr_host =3D socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, > + NETLINK_ROUTE); > + if (nl_sock_linkaddr_host < 0) { > + debug("socket() failed for host: %s", strerror_(errno)); > + return -1; > + } > + > + if (bind(nl_sock_linkaddr_host, (struct sockaddr *)&addr, > + sizeof(addr)) < 0) { > + debug("bind() failed for host: %s", strerror_(errno)); > + close(nl_sock_linkaddr_host); > + nl_sock_linkaddr_host =3D -1; > + return -1; > + } > + > + debug("host socket fd=3D%d", nl_sock_linkaddr_host); > + return 0; > +} > + > +/** > + * nl_linkaddr_notify_init() - Initialize host link/address change notif= ier > + * @c: Execution context > + * > + * In PASTA mode, create a host-side netlink socket to monitor template > + * interface changes and propagate them to the namespace (late binding). > + * > + * Return: 0 on success, -1 on failure > + */ > +int nl_linkaddr_notify_init(const struct ctx *c) > +{ > + union epoll_ref ref =3D { .type =3D EPOLL_TYPE_NL_LINKADDR_HOST }; > + struct epoll_event ev =3D { .events =3D EPOLLIN }; > + > + if (c->mode !=3D MODE_PASTA) > + return 0; > + > + if (nl_sock_linkaddr_host >=3D 0) { > + debug("host notifier already initialized (fd=3D%d)", > + nl_sock_linkaddr_host); > + return 0; > + } > + > + nl_linkaddr_host_init_do(NULL); > + > + if (nl_sock_linkaddr_host < 0) { > + warn("Failed to create host link/addr notifier socket"); > + return -1; > + } > + > + ev.data.u64 =3D ref.u64; > + if (epoll_ctl(c->epollfd, EPOLL_CTL_ADD, > + nl_sock_linkaddr_host, &ev) =3D=3D -1) { > + warn("epoll_ctl() failed on host notifier: %s", > + strerror_(errno)); > + close(nl_sock_linkaddr_host); > + nl_sock_linkaddr_host =3D -1; > + return -1; > + } > + > + info("Host netlink socket fd=3D%d, pasta_ifn=3D%s", > + nl_sock_linkaddr_host, c->pasta_ifn); > + > + return 0; > +} > /** > * nl_sock_init() - Call nl_sock_init_do(), won't return on failure > * @c: Execution context > @@ -516,7 +851,7 @@ int nl_route_set_def(int s, unsigned int ifi, sa_fami= ly_t af, const void *gw) > req.set.r4.rta_gw.rta_len =3D rta_len; > } > =20 > - return nl_do(s, &req, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, len); > + return nl_do(s, &req, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_REPLACE, len); > } > =20 > /** > @@ -927,6 +1262,58 @@ int nl_addr_set(int s, unsigned int ifi, sa_family_= t af, > return nl_do(s, &req, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL, len); > } > =20 > +/** > + * nl_addr_del() - Delete IP address from given interface > + * @s: Netlink socket > + * @ifi: Interface index > + * @af: Address family > + * @addr: Address to delete > + * @prefix_len: Prefix length > + * > + * Return: 0 on success, negative error code on failure > + */ > +static int nl_addr_del(int s, unsigned int ifi, sa_family_t af, > + const void *addr, int prefix_len) > +{ > + struct req_t { > + struct nlmsghdr nlh; > + struct ifaddrmsg ifa; > + union { > + struct { > + struct rtattr rta_l; > + struct in_addr l; > + } a4; > + struct { > + struct rtattr rta_l; > + struct in6_addr l; > + } a6; > + } del; > + } req =3D { > + .ifa.ifa_family =3D af, > + .ifa.ifa_index =3D ifi, > + .ifa.ifa_prefixlen =3D prefix_len, > + }; > + ssize_t len; > + > + if (af =3D=3D AF_INET6) { > + size_t rta_len =3D RTA_LENGTH(sizeof(req.del.a6.l)); > + > + len =3D offsetof(struct req_t, del.a6) + sizeof(req.del.a6); > + memcpy(&req.del.a6.l, addr, sizeof(req.del.a6.l)); > + req.del.a6.rta_l.rta_len =3D rta_len; > + req.del.a6.rta_l.rta_type =3D IFA_LOCAL; > + } else { > + size_t rta_len =3D RTA_LENGTH(sizeof(req.del.a4.l)); > + > + len =3D offsetof(struct req_t, del.a4) + sizeof(req.del.a4); > + memcpy(&req.del.a4.l, addr, sizeof(req.del.a4.l)); > + req.del.a4.rta_l.rta_len =3D rta_len; > + req.del.a4.rta_l.rta_type =3D IFA_LOCAL; > + } > + > + return nl_do(s, &req, RTM_DELADDR, 0, len); > +} > + > /** > * nl_addr_dup() - Copy IP addresses for given interface and address fam= ily > * @s_src: Netlink socket in source network namespace > diff --git a/netlink.h b/netlink.h > index 8f1e9b9..c19d3a3 100644 > --- a/netlink.h > +++ b/netlink.h > @@ -33,4 +33,7 @@ int nl_link_set_flags(int s, unsigned int ifi, > int nl_neigh_notify_init(const struct ctx *c); > void nl_neigh_notify_handler(const struct ctx *c); > =20 > +int nl_linkaddr_notify_init(const struct ctx *c); > +void nl_linkaddr_host_handler(struct ctx *c); > + > #endif /* NETLINK_H */ > diff --git a/passt.c b/passt.c > index 7488a84..64163df 100644 > --- a/passt.c > +++ b/passt.c > @@ -80,6 +80,7 @@ char *epoll_type_str[] =3D { > [EPOLL_TYPE_REPAIR_LISTEN] =3D "TCP_REPAIR helper listening socket", > [EPOLL_TYPE_REPAIR] =3D "TCP_REPAIR helper socket", > [EPOLL_TYPE_NL_NEIGH] =3D "netlink neighbour notifier socket", > + [EPOLL_TYPE_NL_LINKADDR_HOST] =3D "host link/address notifier socket", > }; > static_assert(ARRAY_SIZE(epoll_type_str) =3D=3D EPOLL_NUM_TYPES, > "epoll_type_str[] doesn't match enum epoll_type"); > @@ -303,6 +304,9 @@ static void passt_worker(void *opaque, int nfds, stru= ct epoll_event *events) > case EPOLL_TYPE_NL_NEIGH: > nl_neigh_notify_handler(c); > break; > + case EPOLL_TYPE_NL_LINKADDR_HOST: > + nl_linkaddr_host_handler(c); > + break; > default: > /* Can't happen */ > ASSERT(0); > @@ -413,6 +417,7 @@ int main(int argc, char **argv) > =20 > fwd_neigh_table_init(&c); > nl_neigh_notify_init(&c); > + nl_linkaddr_notify_init(&c); > =20 > if (!c.foreground) { > if ((devnull_fd =3D open("/dev/null", O_RDWR | O_CLOEXEC)) < 0) > --=20 > 2.52.0 >=20 --=20 David Gibson (he or they) | 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 --5ZjgxpLekwo1ZaS6 Content-Type: application/pgp-signature; name=signature.asc -----BEGIN PGP SIGNATURE----- iQIzBAEBCgAdFiEEO+dNsU4E3yXUXRK2zQJF27ox2GcFAmmvmEcACgkQzQJF27ox 2GegHg//Yg8AHsrsBtIBDcBZkO+8yv14bNn5QMcroobCDfQ8WHqI7CBN+5nLADgq Rjqa/EwMbTf21hgRWl5sD24S+ZUc2Slz47na9DDbQ3zuYNBjHDp6neW7J+HBtbtC NLBMm6PxljTNyJ6yCZ87R+V80HM8lQTLG7QUj2zZLY84jdGHgLp+JPBZgGniwIe2 UwKuREMiRJYNgX2wTP7sTNOJWxjmHFKnvTaNmatFTSfAfMrx+aEwv8nF2WZZvQeE 159oKafFHJCt8hQcd5Iz+2O/LYjZYpMtgGKYW5jqkBfYYb4zDVJMoBqW0r+BjJw0 M72eFPIHvx/e0sKb7ewIZzFftOEunUVen23+GLTBbybweQK/Z1Cxrdhn+A2iPAOO xaw4NYsspbxlQeqrWHSmEykWG96tIb5n1DgBPa5BDdOUXWf3pIKX4x6ecCG5vQfZ 0lpdmaDO9DsrEeMDXAe8LvQYypUkrg/Vsyb5mI3lXu7cVsiGPf6EzdScSheDwZ5s XC2XZZ/QzWf7rFMGJS/yoqidaSLowwZbdSuVJBaPmZqe2AO/qFdHChZJj1SEblQH Rz/aQQueUubfXT/sqR0vrB565YlMR1DSDeKuPVR1bphCKcfu2JtbcQUijE3aLd5H Uf/PLOIfe8YgR77C4gmb4ewS5Ch/fuY6Y16IjGk0tJNSxPTHljs= =tXq1 -----END PGP SIGNATURE----- --5ZjgxpLekwo1ZaS6--