diff options
author | mlaier <mlaier@FreeBSD.org> | 2006-06-19 22:20:45 +0000 |
---|---|---|
committer | mlaier <mlaier@FreeBSD.org> | 2006-06-19 22:20:45 +0000 |
commit | f5cde2819f76cb3f86ff02a0c422b289ce94a096 (patch) | |
tree | 1d514a3c44a007d12f9a4007d15e7afe23180bb0 /sys | |
parent | 5a4834b848117e80b174a1d51aff1cfd5e9c8c2d (diff) | |
download | FreeBSD-src-f5cde2819f76cb3f86ff02a0c422b289ce94a096.zip FreeBSD-src-f5cde2819f76cb3f86ff02a0c422b289ce94a096.tar.gz |
Import interface groups from OpenBSD. This allows to group interfaces in
order to - for example - apply firewall rules to a whole group of
interfaces. This is required for importing pf from OpenBSD 3.9
Obtained from: OpenBSD (with changes)
Discussed on: -net (back in April)
Diffstat (limited to 'sys')
-rw-r--r-- | sys/net/if.c | 247 | ||||
-rw-r--r-- | sys/net/if.h | 31 | ||||
-rw-r--r-- | sys/net/if_clone.c | 6 | ||||
-rw-r--r-- | sys/net/if_var.h | 37 | ||||
-rw-r--r-- | sys/sys/sockio.h | 5 |
5 files changed, 326 insertions, 0 deletions
diff --git a/sys/net/if.c b/sys/net/if.c index 8a98fa6..fc8c1cb 100644 --- a/sys/net/if.c +++ b/sys/net/if.c @@ -113,6 +113,8 @@ static int if_rtdel(struct radix_node *, void *); static int ifhwioctl(u_long, struct ifnet *, caddr_t, struct thread *); static void if_start_deferred(void *context, int pending); static void do_link_state_change(void *, int); +static int if_getgroup(struct ifgroupreq *, struct ifnet *); +static int if_getgroupmembers(struct ifgroupreq *); #ifdef INET6 /* * XXX: declare here to avoid to include many inet6 related files.. @@ -125,6 +127,7 @@ int if_index = 0; struct ifindex_entry *ifindex_table = NULL; int ifqmaxlen = IFQ_MAXLEN; struct ifnethead ifnet; /* depend on static init XXX */ +struct ifgrouphead ifg_head; struct mtx ifnet_lock; static if_com_alloc_t *if_com_alloc[256]; static if_com_free_t *if_com_free[256]; @@ -282,6 +285,7 @@ if_init(void *dummy __unused) IFNET_LOCK_INIT(); TAILQ_INIT(&ifnet); + TAILQ_INIT(&ifg_head); knlist_init(&ifklist, NULL, NULL, NULL, NULL); if_grow(); /* create initial table */ ifdev_byindex(0) = make_dev(&net_cdevsw, 0, @@ -441,6 +445,10 @@ if_attach(struct ifnet *ifp) TAILQ_INIT(&ifp->if_addrhead); TAILQ_INIT(&ifp->if_prefixhead); TAILQ_INIT(&ifp->if_multiaddrs); + TAILQ_INIT(&ifp->if_groups); + + if_addgroup(ifp, IFG_ALL); + knlist_init(&ifp->if_klist, NULL, NULL, NULL, NULL); getmicrotime(&ifp->if_lastchange); ifp->if_data.ifi_epoch = time_uptime; @@ -715,6 +723,214 @@ if_detach(struct ifnet *ifp) } /* + * Add a group to an interface + */ +int +if_addgroup(struct ifnet *ifp, const char *groupname) +{ + struct ifg_list *ifgl; + struct ifg_group *ifg = NULL; + struct ifg_member *ifgm; + + if (groupname[0] && groupname[strlen(groupname) - 1] >= '0' && + groupname[strlen(groupname) - 1] <= '9') + return (EINVAL); + + IFNET_WLOCK(); + TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) + if (!strcmp(ifgl->ifgl_group->ifg_group, groupname)) { + IFNET_WUNLOCK(); + return (EEXIST); + } + + if ((ifgl = (struct ifg_list *)malloc(sizeof(struct ifg_list), M_TEMP, + M_NOWAIT)) == NULL) { + IFNET_WUNLOCK(); + return (ENOMEM); + } + + if ((ifgm = (struct ifg_member *)malloc(sizeof(struct ifg_member), + M_TEMP, M_NOWAIT)) == NULL) { + free(ifgl, M_TEMP); + IFNET_WUNLOCK(); + return (ENOMEM); + } + + TAILQ_FOREACH(ifg, &ifg_head, ifg_next) + if (!strcmp(ifg->ifg_group, groupname)) + break; + + if (ifg == NULL) { + if ((ifg = (struct ifg_group *)malloc(sizeof(struct ifg_group), + M_TEMP, M_NOWAIT)) == NULL) { + free(ifgl, M_TEMP); + free(ifgm, M_TEMP); + IFNET_WUNLOCK(); + return (ENOMEM); + } + strlcpy(ifg->ifg_group, groupname, sizeof(ifg->ifg_group)); + ifg->ifg_refcnt = 0; + TAILQ_INIT(&ifg->ifg_members); + EVENTHANDLER_INVOKE(group_attach_event, ifg); + TAILQ_INSERT_TAIL(&ifg_head, ifg, ifg_next); + } + + ifg->ifg_refcnt++; + ifgl->ifgl_group = ifg; + ifgm->ifgm_ifp = ifp; + + IF_ADDR_LOCK(ifp); + TAILQ_INSERT_TAIL(&ifg->ifg_members, ifgm, ifgm_next); + TAILQ_INSERT_TAIL(&ifp->if_groups, ifgl, ifgl_next); + IF_ADDR_UNLOCK(ifp); + + IFNET_WUNLOCK(); + + EVENTHANDLER_INVOKE(group_change_event, groupname); + + return (0); +} + +/* + * Remove a group from an interface + */ +int +if_delgroup(struct ifnet *ifp, const char *groupname) +{ + struct ifg_list *ifgl; + struct ifg_member *ifgm; + + IFNET_WLOCK(); + TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) + if (!strcmp(ifgl->ifgl_group->ifg_group, groupname)) + break; + if (ifgl == NULL) { + IFNET_WUNLOCK(); + return (ENOENT); + } + + IF_ADDR_LOCK(ifp); + TAILQ_REMOVE(&ifp->if_groups, ifgl, ifgl_next); + IF_ADDR_UNLOCK(ifp); + + TAILQ_FOREACH(ifgm, &ifgl->ifgl_group->ifg_members, ifgm_next) + if (ifgm->ifgm_ifp == ifp) + break; + + if (ifgm != NULL) { + TAILQ_REMOVE(&ifgl->ifgl_group->ifg_members, ifgm, ifgm_next); + free(ifgm, M_TEMP); + } + + if (--ifgl->ifgl_group->ifg_refcnt == 0) { + TAILQ_REMOVE(&ifg_head, ifgl->ifgl_group, ifg_next); + EVENTHANDLER_INVOKE(group_detach_event, ifgl->ifgl_group); + free(ifgl->ifgl_group, M_TEMP); + } + IFNET_WUNLOCK(); + + free(ifgl, M_TEMP); + + EVENTHANDLER_INVOKE(group_change_event, groupname); + + return (0); +} + +/* + * Stores all groups from an interface in memory pointed + * to by data + */ +static int +if_getgroup(struct ifgroupreq *data, struct ifnet *ifp) +{ + int len, error; + struct ifg_list *ifgl; + struct ifg_req ifgrq, *ifgp; + struct ifgroupreq *ifgr = data; + + if (ifgr->ifgr_len == 0) { + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) + ifgr->ifgr_len += sizeof(struct ifg_req); + IF_ADDR_UNLOCK(ifp); + return (0); + } + + len = ifgr->ifgr_len; + ifgp = ifgr->ifgr_groups; + /* XXX: wire */ + IF_ADDR_LOCK(ifp); + TAILQ_FOREACH(ifgl, &ifp->if_groups, ifgl_next) { + if (len < sizeof(ifgrq)) { + IF_ADDR_UNLOCK(ifp); + return (EINVAL); + } + bzero(&ifgrq, sizeof ifgrq); + strlcpy(ifgrq.ifgrq_group, ifgl->ifgl_group->ifg_group, + sizeof(ifgrq.ifgrq_group)); + if ((error = copyout(&ifgrq, ifgp, sizeof(struct ifg_req)))) { + IF_ADDR_UNLOCK(ifp); + return (error); + } + len -= sizeof(ifgrq); + ifgp++; + } + IF_ADDR_UNLOCK(ifp); + + return (0); +} + +/* + * Stores all members of a group in memory pointed to by data + */ +static int +if_getgroupmembers(struct ifgroupreq *data) +{ + struct ifgroupreq *ifgr = data; + struct ifg_group *ifg; + struct ifg_member *ifgm; + struct ifg_req ifgrq, *ifgp; + int len, error; + + IFNET_RLOCK(); + TAILQ_FOREACH(ifg, &ifg_head, ifg_next) + if (!strcmp(ifg->ifg_group, ifgr->ifgr_name)) + break; + if (ifg == NULL) { + IFNET_RUNLOCK(); + return (ENOENT); + } + + if (ifgr->ifgr_len == 0) { + TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next) + ifgr->ifgr_len += sizeof(ifgrq); + IFNET_RUNLOCK(); + return (0); + } + + len = ifgr->ifgr_len; + ifgp = ifgr->ifgr_groups; + TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next) { + if (len < sizeof(ifgrq)) { + IFNET_RUNLOCK(); + return (EINVAL); + } + bzero(&ifgrq, sizeof ifgrq); + strlcpy(ifgrq.ifgrq_member, ifgm->ifgm_ifp->if_xname, + sizeof(ifgrq.ifgrq_member)); + if ((error = copyout(&ifgrq, ifgp, sizeof(struct ifg_req)))) { + IFNET_RUNLOCK(); + return (error); + } + len -= sizeof(ifgrq); + ifgp++; + } + IFNET_RUNLOCK(); + + return (0); +} + +/* * Delete Routes for a Network Interface * * Called for each routing entry via the rnh->rnh_walktree() call above @@ -1472,6 +1688,35 @@ ifhwioctl(u_long cmd, struct ifnet *ifp, caddr_t data, struct thread *td) ifr->ifr_addr.sa_data, ifr->ifr_addr.sa_len); break; + case SIOCAIFGROUP: + { + struct ifgroupreq *ifgr = (struct ifgroupreq *)ifr; + + error = suser(td); + if (error) + return (error); + if ((error = if_addgroup(ifp, ifgr->ifgr_group))) + return (error); + break; + } + + case SIOCGIFGROUP: + if ((error = if_getgroup((struct ifgroupreq *)ifr, ifp))) + return (error); + break; + + case SIOCDIFGROUP: + { + struct ifgroupreq *ifgr = (struct ifgroupreq *)ifr; + + error = suser(td); + if (error) + return (error); + if ((error = if_delgroup(ifp, ifgr->ifgr_group))) + return (error); + break; + } + default: error = ENOIOCTL; break; @@ -1511,6 +1756,8 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td) case SIOCIFGCLONERS: return (if_clone_list((struct if_clonereq *)data)); + case SIOCGIFGMEMB: + return (if_getgroupmembers((struct ifgroupreq *)data)); } ifp = ifunit(ifr->ifr_name); diff --git a/sys/net/if.h b/sys/net/if.h index f669017..1116d4b 100644 --- a/sys/net/if.h +++ b/sys/net/if.h @@ -356,6 +356,37 @@ struct ifconf32 { #endif /* + * interface groups + */ + +#define IFG_ALL "all" /* group contains all interfaces */ +/* XXX: will we implement this? */ +#define IFG_EGRESS "egress" /* if(s) default route(s) point to */ + +struct ifg_req { + union { + char ifgrqu_group[IFNAMSIZ]; + char ifgrqu_member[IFNAMSIZ]; + } ifgrq_ifgrqu; +#define ifgrq_group ifgrq_ifgrqu.ifgrqu_group +#define ifgrq_member ifgrq_ifgrqu.ifgrqu_member +}; + +/* + * Used to lookup groups for an interface + */ +struct ifgroupreq { + char ifgr_name[IFNAMSIZ]; + u_int ifgr_len; + union { + char ifgru_group[IFNAMSIZ]; + struct ifg_req *ifgru_groups; + } ifgr_ifgru; +#define ifgr_group ifgr_ifgru.ifgru_group +#define ifgr_groups ifgr_ifgru.ifgru_groups +}; + +/* * Structure for SIOC[AGD]LIFADDR */ struct if_laddrreq { diff --git a/sys/net/if_clone.c b/sys/net/if_clone.c index 89d679d..aa2c21a 100644 --- a/sys/net/if_clone.c +++ b/sys/net/if_clone.c @@ -158,6 +158,8 @@ if_clone_createif(struct if_clone *ifc, char *name, size_t len) if (ifp == NULL) panic("%s: lookup failed for %s", __func__, name); + if_addgroup(ifp, ifc->ifc_name); + IF_CLONE_LOCK(ifc); IFC_IFLIST_INSERT(ifc, ifp); IF_CLONE_UNLOCK(ifc); @@ -210,9 +212,13 @@ if_clone_destroyif(struct if_clone *ifc, struct ifnet *ifp) IFC_IFLIST_REMOVE(ifc, ifp); IF_CLONE_UNLOCK(ifc); + if_delgroup(ifp, ifc->ifc_name); + err = (*ifc->ifc_destroy)(ifc, ifp); if (err != 0) { + if_addgroup(ifp, ifc->ifc_name); + IF_CLONE_LOCK(ifc); IFC_IFLIST_INSERT(ifc, ifp); IF_CLONE_UNLOCK(ifc); diff --git a/sys/net/if_var.h b/sys/net/if_var.h index b76e075..84ac364 100644 --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -91,6 +91,7 @@ TAILQ_HEAD(ifnethead, ifnet); /* we use TAILQs so that the order of */ TAILQ_HEAD(ifaddrhead, ifaddr); /* instantiation is preserved in the list */ TAILQ_HEAD(ifprefixhead, ifprefix); TAILQ_HEAD(ifmultihead, ifmultiaddr); +TAILQ_HEAD(ifgrouphead, ifg_group); /* * Structure defining a queue for a network interface. @@ -182,6 +183,9 @@ struct ifnet { struct task if_linktask; /* task for link change events */ struct mtx if_addr_mtx; /* mutex to protect address lists */ LIST_ENTRY(ifnet) if_clones; /* interfaces of a cloner */ + TAILQ_HEAD(, ifg_list) if_groups; /* linked list of groups per if */ + /* protected by if_addr_mtx */ + void *if_pf_kif; }; typedef void if_init_f_t(void *); @@ -318,6 +322,37 @@ EVENTHANDLER_DECLARE(ifnet_arrival_event, ifnet_arrival_event_handler_t); typedef void (*ifnet_departure_event_handler_t)(void *, struct ifnet *); EVENTHANDLER_DECLARE(ifnet_departure_event, ifnet_departure_event_handler_t); +/* + * interface groups + */ +struct ifg_group { + char ifg_group[IFNAMSIZ]; + u_int ifg_refcnt; + void *ifg_pf_kif; + TAILQ_HEAD(, ifg_member) ifg_members; + TAILQ_ENTRY(ifg_group) ifg_next; +}; + +struct ifg_member { + TAILQ_ENTRY(ifg_member) ifgm_next; + struct ifnet *ifgm_ifp; +}; + +struct ifg_list { + struct ifg_group *ifgl_group; + TAILQ_ENTRY(ifg_list) ifgl_next; +}; + +/* group attach event */ +typedef void (*group_attach_event_handler_t)(void *, struct ifg_group *); +EVENTHANDLER_DECLARE(group_attach_event, group_attach_event_handler_t); +/* group detach event */ +typedef void (*group_detach_event_handler_t)(void *, struct ifg_group *); +EVENTHANDLER_DECLARE(group_detach_event, group_detach_event_handler_t); +/* group change event */ +typedef void (*group_change_event_handler_t)(void *, const char *); +EVENTHANDLER_DECLARE(group_change_event, group_change_event_handler_t); + #define IF_AFDATA_LOCK_INIT(ifp) \ mtx_init(&(ifp)->if_afdata_mtx, "if_afdata", NULL, MTX_DEF) #define IF_AFDATA_LOCK(ifp) mtx_lock(&(ifp)->if_afdata_mtx) @@ -624,6 +659,8 @@ extern int ifqmaxlen; extern struct ifnet *loif; /* first loopback interface */ extern int if_index; +int if_addgroup(struct ifnet *, const char *); +int if_delgroup(struct ifnet *, const char *); int if_addmulti(struct ifnet *, struct sockaddr *, struct ifmultiaddr **); int if_allmulti(struct ifnet *, int); struct ifnet* if_alloc(u_char); diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h index 3331e87..e25aee2 100644 --- a/sys/sys/sockio.h +++ b/sys/sys/sockio.h @@ -117,4 +117,9 @@ #define SIOCIFDESTROY _IOW('i', 121, struct ifreq) /* destroy clone if */ #define SIOCIFGCLONERS _IOWR('i', 120, struct if_clonereq) /* get cloners */ +#define SIOCAIFGROUP _IOW('i', 135, struct ifgroupreq) /* add an ifgroup */ +#define SIOCGIFGROUP _IOWR('i', 136, struct ifgroupreq) /* get ifgroups */ +#define SIOCDIFGROUP _IOW('i', 137, struct ifgroupreq) /* delete ifgroup */ +#define SIOCGIFGMEMB _IOWR('i', 138, struct ifgroupreq) /* get members */ + #endif /* !_SYS_SOCKIO_H_ */ |