aboutsummaryrefslogtreecommitdiff
path: root/sys/net
diff options
context:
space:
mode:
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/altq/altq_cbq.c2
-rw-r--r--sys/net/altq/altq_fairq.c2
-rw-r--r--sys/net/altq/altq_priq.c2
-rw-r--r--sys/net/if.c34
-rw-r--r--sys/net/if_tuntap.c63
-rw-r--r--sys/net/if_var.h8
-rw-r--r--sys/net/pfvar.h9
7 files changed, 100 insertions, 20 deletions
diff --git a/sys/net/altq/altq_cbq.c b/sys/net/altq/altq_cbq.c
index fdf39690160b..2333b9ea8678 100644
--- a/sys/net/altq/altq_cbq.c
+++ b/sys/net/altq/altq_cbq.c
@@ -173,6 +173,8 @@ cbq_request(struct ifaltq *ifq, int req, void *arg)
static void
get_class_stats(class_stats_t *statsp, struct rm_class *cl)
{
+ memset(statsp, 0, sizeof(*statsp));
+
statsp->xmit_cnt = cl->stats_.xmit_cnt;
statsp->drop_cnt = cl->stats_.drop_cnt;
statsp->over = cl->stats_.over;
diff --git a/sys/net/altq/altq_fairq.c b/sys/net/altq/altq_fairq.c
index 6069865101a0..0a00168e547e 100644
--- a/sys/net/altq/altq_fairq.c
+++ b/sys/net/altq/altq_fairq.c
@@ -857,6 +857,8 @@ get_class_stats(struct fairq_classstats *sp, struct fairq_class *cl)
{
fairq_bucket_t *b;
+ memset(sp, 0, sizeof(*sp));
+
sp->class_handle = cl->cl_handle;
sp->qlimit = cl->cl_qlimit;
sp->xmit_cnt = cl->cl_xmitcnt;
diff --git a/sys/net/altq/altq_priq.c b/sys/net/altq/altq_priq.c
index 026346639b2e..fec488418546 100644
--- a/sys/net/altq/altq_priq.c
+++ b/sys/net/altq/altq_priq.c
@@ -597,6 +597,8 @@ priq_purgeq(struct priq_class *cl)
static void
get_class_stats(struct priq_classstats *sp, struct priq_class *cl)
{
+ memset(sp, 0, sizeof(*sp));
+
sp->class_handle = cl->cl_handle;
sp->qlength = qlen(cl->cl_q);
sp->qlimit = qlimit(cl->cl_q);
diff --git a/sys/net/if.c b/sys/net/if.c
index b6a798aa0fab..cb9c47c14c32 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -2842,15 +2842,20 @@ ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td)
break;
case SIOCAIFGROUP:
+ {
+ const char *groupname;
+
error = priv_check(td, PRIV_NET_ADDIFGROUP);
if (error)
return (error);
- error = if_addgroup(ifp,
- ((struct ifgroupreq *)data)->ifgr_group);
+ groupname = ((struct ifgroupreq *)data)->ifgr_group;
+ if (strnlen(groupname, IFNAMSIZ) == IFNAMSIZ)
+ return (EINVAL);
+ error = if_addgroup(ifp, groupname);
if (error != 0)
return (error);
break;
-
+ }
case SIOCGIFGROUP:
{
struct epoch_tracker et;
@@ -2862,15 +2867,20 @@ ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td)
}
case SIOCDIFGROUP:
+ {
+ const char *groupname;
+
error = priv_check(td, PRIV_NET_DELIFGROUP);
if (error)
return (error);
- error = if_delgroup(ifp,
- ((struct ifgroupreq *)data)->ifgr_group);
+ groupname = ((struct ifgroupreq *)data)->ifgr_group;
+ if (strnlen(groupname, IFNAMSIZ) == IFNAMSIZ)
+ return (EINVAL);
+ error = if_delgroup(ifp, groupname);
if (error != 0)
return (error);
break;
-
+ }
default:
error = ENOIOCTL;
break;
@@ -3014,9 +3024,17 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td)
goto out_noref;
case SIOCGIFGMEMB:
- error = if_getgroupmembers((struct ifgroupreq *)data);
- goto out_noref;
+ {
+ struct ifgroupreq *req;
+ req = (struct ifgroupreq *)data;
+ if (strnlen(req->ifgr_name, IFNAMSIZ) == IFNAMSIZ) {
+ error = EINVAL;
+ goto out_noref;
+ }
+ error = if_getgroupmembers(req);
+ goto out_noref;
+ }
#if defined(INET) || defined(INET6)
case SIOCSVH:
case SIOCGVH:
diff --git a/sys/net/if_tuntap.c b/sys/net/if_tuntap.c
index 56bb90cce9bc..0dc3a58f6ae6 100644
--- a/sys/net/if_tuntap.c
+++ b/sys/net/if_tuntap.c
@@ -138,6 +138,7 @@ struct tuntap_softc {
#define TUN_READY (TUN_OPEN | TUN_INITED)
pid_t tun_pid; /* owning pid */
+ struct epoch_context tun_epoch_ctx;
struct ifnet *tun_ifp; /* the interface */
struct sigio *tun_sigio; /* async I/O info */
struct tuntap_driver *tun_drv; /* appropriate driver */
@@ -630,6 +631,18 @@ out:
CURVNET_RESTORE();
}
+static void
+tunfree(struct epoch_context *ctx)
+{
+ struct tuntap_softc *tp;
+
+ tp = __containerof(ctx, struct tuntap_softc, tun_epoch_ctx);
+
+ /* Any remaining resources that would be needed by a concurrent open. */
+ mtx_destroy(&tp->tun_mtx);
+ free(tp, M_TUN);
+}
+
static int
tun_destroy(struct tuntap_softc *tp, bool may_intr)
{
@@ -649,7 +662,7 @@ tun_destroy(struct tuntap_softc *tp, bool may_intr)
error = cv_wait_sig(&tp->tun_cv, &tp->tun_mtx);
else
cv_wait(&tp->tun_cv, &tp->tun_mtx);
- if (error != 0) {
+ if (error != 0 && tp->tun_busy != 0) {
tp->tun_flags &= ~TUN_DYING;
TUN_UNLOCK(tp);
return (error);
@@ -663,8 +676,18 @@ tun_destroy(struct tuntap_softc *tp, bool may_intr)
TAILQ_REMOVE(&tunhead, tp, tun_list);
mtx_unlock(&tunmtx);
- /* destroy_dev will take care of any alias. */
- destroy_dev(tp->tun_dev);
+ /*
+ * destroy_dev will take care of any alias. For transient tunnels,
+ * we're being called from close(2) so we can't destroy it ourselves
+ * without deadlocking, but we already know that we can cleanup
+ * everything else and just continue to prevent it from being reopened.
+ */
+ if ((tp->tun_flags & TUN_TRANSIENT) != 0) {
+ atomic_store_ptr(&tp->tun_dev->si_drv1, tp->tun_dev);
+ destroy_dev_sched(tp->tun_dev);
+ } else {
+ destroy_dev(tp->tun_dev);
+ }
seldrain(&tp->tun_rsel);
knlist_clear(&tp->tun_rsel.si_note, 0);
knlist_destroy(&tp->tun_rsel.si_note);
@@ -679,9 +702,8 @@ tun_destroy(struct tuntap_softc *tp, bool may_intr)
sx_xunlock(&tun_ioctl_sx);
free_unr(tp->tun_drv->unrhdr, TUN2IFP(tp)->if_dunit);
if_free(TUN2IFP(tp));
- mtx_destroy(&tp->tun_mtx);
cv_destroy(&tp->tun_cv);
- free(tp, M_TUN);
+ NET_EPOCH_CALL(tunfree, &tp->tun_epoch_ctx);
CURVNET_RESTORE();
return (0);
@@ -742,9 +764,11 @@ tun_uninit(const void *unused __unused)
mtx_unlock(&tunmtx);
for (i = 0; i < nitems(tuntap_drivers); ++i) {
drv = &tuntap_drivers[i];
+ destroy_dev_drain(&drv->cdevsw);
delete_unrhdr(drv->unrhdr);
clone_cleanup(&drv->clones);
}
+ NET_EPOCH_DRAIN_CALLBACKS();
mtx_destroy(&tunmtx);
}
SYSUNINIT(tun_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, tun_uninit, NULL);
@@ -1104,19 +1128,43 @@ out:
static int
tunopen(struct cdev *dev, int flag, int mode, struct thread *td)
{
+ struct epoch_tracker et;
struct ifnet *ifp;
struct tuntap_softc *tp;
+ void *p;
int error __diagused, tunflags;
+ /*
+ * Transient tunnels do deferred destroy of the tun device but want
+ * to immediately cleanup state, so they clobber si_drv1 to avoid a
+ * use-after-free in case someone does happen to open it in the interim.
+ * We avoid using NULL to be able to distinguish from an uninitialized
+ * cdev.
+ *
+ * We use the net epoch here to let a concurrent tun_destroy() schedule
+ * freeing our tuntap_softc, in case we entered here and loaded si_drv1
+ * before it was swapped out. If we managed to load this while it was
+ * still a softc, then the concurrent tun_destroy() hasn't yet scheduled
+ * it to be free- that will take place sometime after the epoch we just
+ * entered, so we can safely use it.
+ */
+ NET_EPOCH_ENTER(et);
+ p = atomic_load_ptr(&dev->si_drv1);
+ if (p == dev) {
+ NET_EPOCH_EXIT(et);
+ return (ENXIO);
+ }
+
tunflags = 0;
CURVNET_SET(TD_TO_VNET(td));
error = tuntap_name2info(dev->si_name, NULL, &tunflags);
if (error != 0) {
CURVNET_RESTORE();
+ NET_EPOCH_EXIT(et);
return (error); /* Shouldn't happen */
}
- tp = dev->si_drv1;
+ tp = p;
KASSERT(tp != NULL,
("si_drv1 should have been initialized at creation"));
@@ -1124,14 +1172,17 @@ tunopen(struct cdev *dev, int flag, int mode, struct thread *td)
if ((tp->tun_flags & TUN_INITED) == 0) {
TUN_UNLOCK(tp);
CURVNET_RESTORE();
+ NET_EPOCH_EXIT(et);
return (ENXIO);
}
if ((tp->tun_flags & (TUN_OPEN | TUN_DYING)) != 0) {
TUN_UNLOCK(tp);
CURVNET_RESTORE();
+ NET_EPOCH_EXIT(et);
return (EBUSY);
}
+ NET_EPOCH_EXIT(et);
error = tun_busy_locked(tp);
KASSERT(error == 0, ("Must be able to busy an unopen tunnel"));
ifp = TUN2IFP(tp);
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index f2df612b19c1..961259bb0ca1 100644
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -383,18 +383,18 @@ struct ifg_group {
char ifg_group[IFNAMSIZ];
u_int ifg_refcnt;
void *ifg_pf_kif;
- CK_STAILQ_HEAD(, ifg_member) ifg_members; /* (CK_) */
- CK_STAILQ_ENTRY(ifg_group) ifg_next; /* (CK_) */
+ CK_STAILQ_HEAD(, ifg_member) ifg_members;
+ CK_STAILQ_ENTRY(ifg_group) ifg_next;
};
struct ifg_member {
- CK_STAILQ_ENTRY(ifg_member) ifgm_next; /* (CK_) */
+ CK_STAILQ_ENTRY(ifg_member) ifgm_next;
if_t ifgm_ifp;
};
struct ifg_list {
struct ifg_group *ifgl_group;
- CK_STAILQ_ENTRY(ifg_list) ifgl_next; /* (CK_) */
+ CK_STAILQ_ENTRY(ifg_list) ifgl_next;
};
#ifdef _SYS_EVENTHANDLER_H_
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 8aefe514946e..ce266a267f3c 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -2038,14 +2038,15 @@ struct pfioc_trans {
} *array;
};
-#define PFR_FLAG_ATOMIC 0x00000001 /* unused */
+#define PFR_FLAG_START 0x00000001
#define PFR_FLAG_DUMMY 0x00000002
#define PFR_FLAG_FEEDBACK 0x00000004
#define PFR_FLAG_CLSTATS 0x00000008
#define PFR_FLAG_ADDRSTOO 0x00000010
#define PFR_FLAG_REPLACE 0x00000020
#define PFR_FLAG_ALLRSETS 0x00000040
-#define PFR_FLAG_ALLMASK 0x0000007F
+#define PFR_FLAG_DONE 0x00000080
+#define PFR_FLAG_ALLMASK 0x000000FF
#ifdef _KERNEL
#define PFR_FLAG_USERIOCTL 0x10000000
#endif
@@ -2435,6 +2436,7 @@ extern struct pf_ksrc_node *pf_find_src_node(struct pf_addr *,
struct pf_srchash **, pf_sn_types_t, bool);
extern void pf_unlink_src_node(struct pf_ksrc_node *);
extern u_int pf_free_src_nodes(struct pf_ksrc_node_list *);
+extern void pf_free_src_node(struct pf_ksrc_node *);
extern void pf_print_state(struct pf_kstate *);
extern void pf_print_flags(uint16_t);
extern int pf_addr_wrap_neq(struct pf_addr_wrap *,
@@ -2521,6 +2523,9 @@ uint16_t pf_qname2qid(const char *, bool);
void pfr_initialize(void);
void pfr_cleanup(void);
+struct pfr_kentry *
+ pfr_kentry_byaddr(struct pfr_ktable *, struct pf_addr *, sa_family_t,
+ int);
int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t);
void pfr_update_stats(struct pfr_ktable *, struct pf_addr *, sa_family_t,
u_int64_t, int, int, int);