Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F175708140
D60594.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
D60594.diff
View Options
diff --git a/usr.sbin/rtsold/rtsol.c b/usr.sbin/rtsold/rtsol.c
--- a/usr.sbin/rtsold/rtsol.c
+++ b/usr.sbin/rtsold/rtsol.c
@@ -58,6 +58,7 @@
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
+#include <stdbool.h>
#include <stdio.h>
#include <time.h>
#include <err.h>
@@ -71,6 +72,7 @@
struct ifinfo_head_t ifinfo_head = TAILQ_HEAD_INITIALIZER(ifinfo_head);
static void call_script(const char *const *, struct script_msg_head_t *);
+static void captive_portal_set(struct ifinfo *, const char *);
static size_t dname_labeldec(char *, size_t, const char *);
static struct ra_opt *find_raopt(struct rainfo *, int, void *, size_t);
static int ra_opt_rdnss_dispatch(struct ifinfo *, struct rainfo *,
@@ -171,7 +173,7 @@
struct nd_router_advert *nd_ra;
struct cmsghdr *cm;
struct rainfo *rai;
- char *p, *raoptp;
+ char *p, *raoptp, *cp;
struct in6_addr *addr;
struct nd_opt_hdr *ndo;
struct nd_opt_rdnss *rdnss;
@@ -180,6 +182,8 @@
char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1];
char dname[NI_MAXHOST];
struct timespec lifetime, now;
+ char *capurl;
+ bool complete;
int newent_rai, newent_rao;
memset(&hdr, 0, sizeof(hdr));
@@ -339,6 +343,8 @@
clock_gettime(CLOCK_MONOTONIC_FAST, &now);
newent_rai = 0;
+ capurl = NULL;
+ complete = true;
rai = find_rainfo(ifi, &from);
if (rai == NULL) {
ELM_MALLOC(rai, exit(1));
@@ -366,10 +372,12 @@
if (ndo->nd_opt_len == 0) {
warnmsg(LOG_INFO, __func__, "invalid option length 0.");
+ complete = false;
break;
}
if ((char *)RA_OPT_NEXT_HDR(raoptp) > (char *)icp + msglen) {
warnmsg(LOG_INFO, __func__, "option length overflow.");
+ complete = false;
break;
}
@@ -515,12 +523,64 @@
p += len;
}
break;
+ case ND_OPT_CAPTIVE_PORTAL:
+ /*
+ * RFC 8910: the option carries the captive portal
+ * API URL, zero padded to the 8-octet boundary.
+ * It has no lifetime of its own: the URL is
+ * considered present as long as the router keeps
+ * advertising it, so it is recorded per router and
+ * replaced once the whole RA has been parsed.
+ */
+ len = ndo->nd_opt_len * 8 - sizeof(*ndo);
+ if (len == 0) {
+ warnmsg(LOG_INFO, __func__,
+ "empty captive portal option in RA "
+ "from %s was ignored.",
+ inet_ntop(AF_INET6, &from.sin6_addr,
+ ntopbuf, sizeof(ntopbuf)));
+ break;
+ }
+ if ((cp = malloc(len + 1)) == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "malloc failed: %s", strerror(errno));
+ break;
+ }
+ memcpy(cp, ndo + 1, len);
+ cp[len] = '\0';
+ while (len > 0 && cp[len - 1] == '\0')
+ cp[--len] = '\0';
+ if (len == 0) {
+ free(cp);
+ break;
+ }
+ warnmsg(LOG_DEBUG, __func__, "captive portal = %s",
+ cp);
+ if (capurl == NULL)
+ capurl = cp;
+ else
+ free(cp);
+ break;
default:
/* nothing to do for other options */
break;
}
raoptp = (char *)RA_OPT_NEXT_HDR(raoptp);
}
+
+ /*
+ * The RA just parsed is authoritative for the captive portal URL
+ * of this router: replace the previously stored one, or drop it
+ * when this RA does not carry the option. A truncated RA tells
+ * nothing about a missing option, so keep the current value then.
+ */
+ if (complete) {
+ free(rai->rai_captive_url);
+ rai->rai_captive_url = capurl;
+ capurl = NULL;
+ }
+ free(capurl);
+
if (newent_rai)
TAILQ_INSERT_TAIL(&ifi->ifi_rainfo, rai, rai_next);
@@ -555,9 +615,11 @@
TAILQ_HEAD_INITIALIZER(sm_rdnss_head);
struct script_msg_head_t sm_dnssl_head =
TAILQ_HEAD_INITIALIZER(sm_dnssl_head);
+ char *captive;
int dcount, dlen;
+ captive = NULL;
dcount = 0;
dlen = strlen(resstr_sh_prefix) + strlen(resstr_nl);
clock_gettime(CLOCK_MONOTONIC_FAST, &now);
@@ -568,6 +630,8 @@
* handled, not overridden. [RFC 4861 6.3.4]
*/
TAILQ_FOREACH(rai, &ifi->ifi_rainfo, rai_next) {
+ if (captive == NULL)
+ captive = rai->rai_captive_url;
TAILQ_FOREACH(rao, &rai->rai_ra_opt, rao_next) {
switch (rao->rao_type) {
case ND_OPT_RDNSS:
@@ -647,6 +711,7 @@
if (!uflag)
ra_opt_rdnss_dispatch(ifi, NULL, &sm_rdnss_head,
&sm_dnssl_head);
+ captive_portal_set(ifi, captive);
return (0);
}
@@ -776,6 +841,73 @@
argv[0], status);
}
+/*
+ * Advertise the captive portal API URL received in a Router
+ * Advertisement (RFC 8910).
+ */
+static void
+captive_portal_set(struct ifinfo *ifi, const char *url)
+{
+ char name[IFNAMSIZ + 32], tmp[MAXPATHLEN];
+ int fd;
+ ssize_t len;
+
+ if (captivefd < 0)
+ return;
+
+ if (url == NULL) {
+ if (ifi->ifi_captive_url == NULL)
+ return;
+ snprintf(name, sizeof(name), "captive-portal6.%s",
+ ifi->ifname);
+ (void)unlinkat(captivefd, name, 0);
+ free(ifi->ifi_captive_url);
+ ifi->ifi_captive_url = NULL;
+ return;
+ }
+
+ if (ifi->ifi_captive_url != NULL &&
+ strcmp(ifi->ifi_captive_url, url) == 0)
+ return;
+
+ /*
+ * Remove a stale temporary file left by an interrupted run, then
+ * create it with O_EXCL so a symlink cannot be followed.
+ */
+ snprintf(name, sizeof(name), "captive-portal6.%s", ifi->ifname);
+ snprintf(tmp, sizeof(tmp), "%s.tmp", name);
+ (void)unlinkat(captivefd, tmp, 0);
+ fd = openat(captivefd, tmp, O_WRONLY | O_CREAT | O_EXCL, 0644);
+ if (fd < 0) {
+ warnmsg(LOG_ERR, __func__, "openat(%s): %s", tmp,
+ strerror(errno));
+ return;
+ }
+ len = strlen(url);
+ if (write(fd, url, len) != len || write(fd, "\n", 1) != 1) {
+ warnmsg(LOG_ERR, __func__, "write(%s): %s", tmp,
+ strerror(errno));
+ (void)close(fd);
+ (void)unlinkat(captivefd, tmp, 0);
+ return;
+ }
+ (void)close(fd);
+ if (renameat(captivefd, tmp, captivefd, name) < 0) {
+ warnmsg(LOG_ERR, __func__, "renameat(%s): %s", name,
+ strerror(errno));
+ (void)unlinkat(captivefd, tmp, 0);
+ return;
+ }
+
+ free(ifi->ifi_captive_url);
+ ifi->ifi_captive_url = strdup(url);
+ if (ifi->ifi_captive_url == NULL)
+ return;
+
+ warnmsg(LOG_INFO, __func__, "New Captive Portal (%s): %s",
+ ifi->ifname, url);
+}
+
#define PERIOD 0x2e
#define hyphenchar(c) ((c) == 0x2d)
#define periodchar(c) ((c) == PERIOD)
diff --git a/usr.sbin/rtsold/rtsold.h b/usr.sbin/rtsold/rtsold.h
--- a/usr.sbin/rtsold/rtsold.h
+++ b/usr.sbin/rtsold/rtsold.h
@@ -56,6 +56,8 @@
struct ifinfo *rai_ifinfo;
struct sockaddr_in6 rai_saddr;
TAILQ_HEAD(, ra_opt) rai_ra_opt;
+ char *rai_captive_url; /* captive portal API URL,
+ * or NULL */
};
/* Per-interface tracking info. */
@@ -84,6 +86,8 @@
int ifi_rdnss; /* RDNSS option state */
int ifi_dnssl; /* DNSSL option state */
+ char *ifi_captive_url; /* captive portal API URL, or NULL */
+
int racnt; /* total # of valid RAs it have got */
TAILQ_HEAD(, rainfo) ifi_rainfo;
@@ -161,6 +165,7 @@
extern const char *otherconf_script;
extern const char *alwaysconf_script;
extern const char *resolvconf_script;
+extern int captivefd;
extern struct cap_channel *capllflags, *capscript, *capsendmsg, *capsyslog;
struct ifinfo *find_ifinfo(int);
diff --git a/usr.sbin/rtsold/rtsold.8 b/usr.sbin/rtsold/rtsold.8
--- a/usr.sbin/rtsold/rtsold.8
+++ b/usr.sbin/rtsold/rtsold.8
@@ -294,6 +294,10 @@
.Pp
Otherwise it will be
.Ql ifname:slaac .
+.Pp
+When a Router Advertisement carries a Captive Portal option (option 37,
+RFC 8910), the advertised API URL is written to
+.Pa /var/run/captive-portal6. Ns Ar interface
.El
.Sh FILES
.Bl -tag -width /var/run/rtsold.dump -compact
@@ -302,6 +306,11 @@
.Nm .
.It Pa /var/run/rtsold.dump
Internal state dump file.
+.It Pa /var/run/captive-portal6. Ns Ar interface
+The Captive Portal API URL received in a Router Advertisement,
+terminated by a newline.
+The file is removed when a subsequent Router Advertisement from the
+same router no longer carries the Captive Portal option.
.El
.\"
.Sh EXIT STATUS
diff --git a/usr.sbin/rtsold/rtsold.c b/usr.sbin/rtsold/rtsold.c
--- a/usr.sbin/rtsold/rtsold.c
+++ b/usr.sbin/rtsold/rtsold.c
@@ -83,6 +83,8 @@
const char *alwaysconf_script;
const char *resolvconf_script = "/sbin/resolvconf";
+int captivefd = -1;
+
cap_channel_t *capllflags, *capscript, *capsendmsg, *capsyslog;
/* protocol constants */
@@ -282,6 +284,24 @@
exit(1);
}
+ /*
+ * Open the directory where the captive portal API URL (RFC 8910)
+ * is published before entering capability mode. The feature is
+ * disabled if the descriptor cannot be opened or limited.
+ */
+ captivefd = open("/var/run", O_RDONLY | O_DIRECTORY);
+ if (captivefd >= 0) {
+ cap_rights_t rights;
+
+ cap_rights_init(&rights, CAP_LOOKUP, CAP_CREATE, CAP_WRITE,
+ CAP_FTRUNCATE, CAP_RENAMEAT_SOURCE, CAP_RENAMEAT_TARGET,
+ CAP_UNLINKAT);
+ if (caph_rights_limit(captivefd, &rights) < 0) {
+ (void)close(captivefd);
+ captivefd = -1;
+ }
+ }
+
/* Enter capability mode. */
caph_cache_catpages();
if (caph_enter_casper() != 0) {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Oct 13, 4:32 PM (12 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40624642
Default Alt Text
D60594.diff (8 KB)
Attached To
Mode
D60594: rtsold: add Captive Portal (RFC 8910) support
Attached
Detach File
Event Timeline
Log In to Comment