From 0a887f91f9633448c99b9a5b7c6116a0a22d25d6 Mon Sep 17 00:00:00 2001 From: Aaron LI Date: Sat, 16 Jun 2018 23:18:20 +0800 Subject: [PATCH] pf: Allow disappearing or not yet existing interfaces for ALTQ Make ALTQ cope with disappearing interfaces (particularly common with net/mpd4 and netgraph in general). This also allows to add queues for an interface that is not yet existing, however, you have to provide the bandwidth for the interface. Meanwhile, simplify the ifnet_unlock() calls for ifunit() use. Taken-from: FreeBSD (r177700) --- sys/net/pf/pf_if.c | 10 +++ sys/net/pf/pf_ioctl.c | 123 ++++++++++++++++++++++++++++------ sys/net/pf/pfvar.h | 6 ++ usr.sbin/pfctl/pfctl_altq.c | 4 ++ usr.sbin/pfctl/pfctl_qstats.c | 17 ++++- 5 files changed, 139 insertions(+), 21 deletions(-) diff --git a/sys/net/pf/pf_if.c b/sys/net/pf/pf_if.c index 18fb39c3f5..b623871448 100644 --- a/sys/net/pf/pf_if.c +++ b/sys/net/pf/pf_if.c @@ -866,12 +866,22 @@ static void pfi_attach_ifnet_event(void *arg __unused, struct ifnet *ifp) { pfi_attach_ifnet(ifp); +#ifdef ALTQ + crit_enter(); + pf_altq_ifnet_event(ifp, 0); + crit_exit(); +#endif } static void pfi_detach_ifnet_event(void *arg __unused, struct ifnet *ifp) { pfi_detach_ifnet(ifp); +#ifdef ALTQ + crit_enter(); + pf_altq_ifnet_event(ifp, 1); + crit_exit(); +#endif } static void diff --git a/sys/net/pf/pf_ioctl.c b/sys/net/pf/pf_ioctl.c index afdc9b0e56..8b407c9776 100644 --- a/sys/net/pf/pf_ioctl.c +++ b/sys/net/pf/pf_ioctl.c @@ -565,7 +565,8 @@ pf_begin_altq(u_int32_t *ticket) /* Purge the old altq list */ while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { TAILQ_REMOVE(pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0) { + if (altq->qname[0] == 0 && + (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { /* detach and destroy the discipline */ error = altq_remove(altq); } else @@ -590,7 +591,8 @@ pf_rollback_altq(u_int32_t ticket) /* Purge the old altq list */ while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { TAILQ_REMOVE(pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0) { + if (altq->qname[0] == 0 && + (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { /* detach and destroy the discipline */ error = altq_remove(altq); } else @@ -620,7 +622,8 @@ pf_commit_altq(u_int32_t ticket) /* Attach new disciplines */ TAILQ_FOREACH(altq, pf_altqs_active, entries) { - if (altq->qname[0] == 0) { + if (altq->qname[0] == 0 && + (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { /* attach the discipline */ error = altq_pfattach(altq); if (error) { @@ -633,7 +636,8 @@ pf_commit_altq(u_int32_t ticket) /* Purge the old altq list */ while ((altq = TAILQ_FIRST(pf_altqs_inactive)) != NULL) { TAILQ_REMOVE(pf_altqs_inactive, altq, entries); - if (altq->qname[0] == 0) { + if (altq->qname[0] == 0 && + (altq->local_flags & PFALTQ_FLAG_IF_REMOVED) == 0) { /* detach and destroy the discipline */ if (pf_altq_running) error = pf_disable_altq(altq); @@ -661,11 +665,11 @@ pf_enable_altq(struct pf_altq *altq) int error = 0; ifnet_lock(); + ifp = ifunit(altq->ifname); + ifnet_unlock(); - if ((ifp = ifunit(altq->ifname)) == NULL) { - ifnet_unlock(); + if (ifp == NULL) return (EINVAL); - } if (ifp->if_snd.altq_type != ALTQT_NONE) error = altq_enable(&ifp->if_snd); @@ -679,7 +683,6 @@ pf_enable_altq(struct pf_altq *altq) crit_exit(); } - ifnet_unlock(); return (error); } @@ -691,20 +694,18 @@ pf_disable_altq(struct pf_altq *altq) int error; ifnet_lock(); + ifp = ifunit(altq->ifname); + ifnet_unlock(); - if ((ifp = ifunit(altq->ifname)) == NULL) { - ifnet_unlock(); + if (ifp == NULL) return (EINVAL); - } /* * when the discipline is no longer referenced, it was overridden * by a new one. if so, just return. */ - if (altq->altq_disc != ifp->if_snd.altq_disc) { - ifnet_unlock(); + if (altq->altq_disc != ifp->if_snd.altq_disc) return (0); - } error = altq_disable(&ifp->if_snd); @@ -716,9 +717,76 @@ pf_disable_altq(struct pf_altq *altq) crit_exit(); } - ifnet_unlock(); return (error); } + +void +pf_altq_ifnet_event(struct ifnet *ifp, int remove) +{ + struct ifnet *ifp1; + struct pf_altq *a1, *a2, *a3; + u_int32_t ticket; + int error = 0; + + /* Interrupt userland queue modifications */ + if (altqs_inactive_open) + pf_rollback_altq(ticket_altqs_inactive); + + /* Start new altq ruleset */ + if (pf_begin_altq(&ticket)) + return; + + /* Copy the current active set */ + TAILQ_FOREACH(a1, pf_altqs_active, entries) { + a2 = kmalloc(sizeof(*a2), M_PFALTQPL, M_INTWAIT); + if (a2 == NULL) { + error = ENOMEM; + break; + } + bcopy(a1, a2, sizeof(struct pf_altq)); + + if (a2->qname[0] != 0) { + if ((a2->qid = pf_qname2qid(a2->qname)) == 0) { + error = EBUSY; + kfree(a2, M_PFALTQPL); + break; + } + a2->altq_disc = NULL; + TAILQ_FOREACH(a3, pf_altqs_inactive, entries) { + if (strncmp(a3->ifname, a2->ifname, + IFNAMSIZ) == 0 && a3->qname[0] == 0) { + a2->altq_disc = a3->altq_disc; + break; + } + } + } + /* Deactivate the interface in question */ + a2->local_flags &= ~PFALTQ_FLAG_IF_REMOVED; + ifnet_lock(); + ifp1 = ifunit(a2->ifname); + ifnet_unlock(); + if ((ifp1 == NULL) || (remove && ifp1 == ifp)) { + a2->local_flags |= PFALTQ_FLAG_IF_REMOVED; + } else { + error = altq_add(a2); + + if (ticket != ticket_altqs_inactive) + error = EBUSY; + + if (error) { + kfree(a2, M_PFALTQPL); + break; + } + } + + TAILQ_INSERT_TAIL(pf_altqs_inactive, a2, entries); + } + + if (error != 0) + pf_rollback_altq(ticket); + else + pf_commit_altq(ticket); +} #endif /* ALTQ */ int @@ -1918,11 +1986,11 @@ pfioctl(struct dev_ioctl_args *ap) strlcpy(ps.ifname, psp->ifname, IFNAMSIZ); ifnet_lock(); ifp = ifunit(ps.ifname); - if (ifp ) + ifnet_unlock(); + if (ifp) psp->baudrate = ifp->if_baudrate; else error = EINVAL; - ifnet_unlock(); } else error = EINVAL; break; @@ -1932,8 +2000,10 @@ pfioctl(struct dev_ioctl_args *ap) struct pf_altq *altq; /* enable all altq interfaces on active list */ + /* XXX: locking? */ TAILQ_FOREACH(altq, pf_altqs_active, entries) { - if (altq->qname[0] == 0) { + if (altq->qname[0] == 0 && (altq->local_flags & + PFALTQ_FLAG_IF_REMOVED) == 0) { error = pf_enable_altq(altq); if (error != 0) break; @@ -1950,7 +2020,8 @@ pfioctl(struct dev_ioctl_args *ap) /* disable all altq interfaces on active list */ TAILQ_FOREACH(altq, pf_altqs_active, entries) { - if (altq->qname[0] == 0) { + if (altq->qname[0] == 0 && (altq->local_flags & + PFALTQ_FLAG_IF_REMOVED) == 0) { error = pf_disable_altq(altq); if (error != 0) break; @@ -1965,6 +2036,7 @@ pfioctl(struct dev_ioctl_args *ap) case DIOCADDALTQ: { struct pfioc_altq *pa = (struct pfioc_altq *)addr; struct pf_altq *altq, *a; + struct ifnet *ifp; if (pa->ticket != ticket_altqs_inactive) { error = EBUSY; @@ -1997,7 +2069,14 @@ pfioctl(struct dev_ioctl_args *ap) } } - error = altq_add(altq); + ifnet_lock(); + ifp = ifunit(altq->ifname); + ifnet_unlock(); + if (ifp == NULL) + altq->local_flags |= PFALTQ_FLAG_IF_REMOVED; + else + error = altq_add(altq); + if (error) { kfree(altq, M_PFALTQPL); break; @@ -2068,6 +2147,10 @@ pfioctl(struct dev_ioctl_args *ap) error = EBUSY; break; } + if ((altq->local_flags & PFALTQ_FLAG_IF_REMOVED) != 0) { + error = ENXIO; + break; + } error = altq_getqstats(altq, pq->buf, &nbytes); if (error == 0) { pq->scheduler = altq->scheduler; diff --git a/sys/net/pf/pfvar.h b/sys/net/pf/pfvar.h index 91bb0cc187..a2e71acb57 100644 --- a/sys/net/pf/pfvar.h +++ b/sys/net/pf/pfvar.h @@ -1434,6 +1434,9 @@ struct pf_altq { u_int32_t parent_qid; /* parent queue id */ u_int32_t bandwidth; /* queue bandwidth */ u_int8_t priority; /* priority */ + uint8_t local_flags; /* dynamic interface */ +#define PFALTQ_FLAG_IF_REMOVED 0x01 + u_int16_t qlimit; /* queue size limit */ u_int16_t flags; /* misc flags */ union { @@ -1769,6 +1772,9 @@ extern int pf_tbladdr_setup(struct pf_ruleset *, extern void pf_tbladdr_remove(struct pf_addr_wrap *); extern void pf_tbladdr_copyout(struct pf_addr_wrap *); extern void pf_calc_skip_steps(struct pf_rulequeue *); +#ifdef ALTQ +extern void pf_altq_ifnet_event(struct ifnet *, int); +#endif extern struct malloc_type *pf_src_tree_pl, *pf_rule_pl; extern struct malloc_type *pf_state_pl, *pf_state_key_pl, *pf_state_item_pl, *pf_altq_pl, *pf_pooladdr_pl; diff --git a/usr.sbin/pfctl/pfctl_altq.c b/usr.sbin/pfctl/pfctl_altq.c index de21e33055..2064bb0e49 100644 --- a/usr.sbin/pfctl/pfctl_altq.c +++ b/usr.sbin/pfctl/pfctl_altq.c @@ -156,6 +156,8 @@ print_altq(const struct pf_altq *a, unsigned int level, return; } + if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) + printf("INACTIVE "); printf("altq on %s ", a->ifname); switch (a->scheduler) { @@ -195,6 +197,8 @@ print_queue(const struct pf_altq *a, unsigned int level, { unsigned int i; + if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) + printf("INACTIVE "); printf("queue "); for (i = 0; i < level; ++i) printf(" "); diff --git a/usr.sbin/pfctl/pfctl_qstats.c b/usr.sbin/pfctl/pfctl_qstats.c index 4a22c2fee8..033897a701 100644 --- a/usr.sbin/pfctl/pfctl_qstats.c +++ b/usr.sbin/pfctl/pfctl_qstats.c @@ -96,6 +96,8 @@ pfctl_show_altq(int dev, const char *iface, int opts, int verbose2) for (node = root; node != NULL; node = node->next) { if (iface != NULL && strcmp(node->altq.ifname, iface)) continue; + if (node->altq.local_flags & PFALTQ_FLAG_IF_REMOVED) + continue; if (dotitle) { pfctl_print_title("ALTQ:"); dotitle = 0; @@ -151,7 +153,8 @@ pfctl_update_qstats(int dev, struct pf_altq_node **root) warn("DIOCGETALTQ"); return (-1); } - if (pa.altq.qid > 0) { + if ((pa.altq.qid > 0) && + !(pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED)) { pq.nr = nr; pq.ticket = pa.ticket; pq.buf = &qstats.data; @@ -168,6 +171,16 @@ pfctl_update_qstats(int dev, struct pf_altq_node **root) } else { pfctl_insert_altq_node(root, pa.altq, qstats); } + } else if (pa.altq.local_flags & PFALTQ_FLAG_IF_REMOVED) { + memset(&qstats.data, 0, sizeof(qstats.data)); + if ((node = pfctl_find_altq_node(*root, pa.altq.qname, + pa.altq.ifname)) != NULL) { + memcpy(&node->qstats.data, &qstats.data, + sizeof(qstats.data)); + update_avg(node); + } else { + pfctl_insert_altq_node(root, pa.altq, qstats); + } } } return (mnr); @@ -274,6 +287,8 @@ pfctl_print_altq_nodestat(int dev __unused, const struct pf_altq_node *a) { if (a->altq.qid == 0) return; + if (a->altq.local_flags & PFALTQ_FLAG_IF_REMOVED) + return; switch (a->altq.scheduler) { case ALTQT_CBQ: -- 2.41.0