Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F145473877
D51238.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
14 KB
Referenced Files
None
Subscribers
None
D51238.diff
View Options
diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c
--- a/sbin/ifconfig/ifbridge.c
+++ b/sbin/ifconfig/ifbridge.c
@@ -48,6 +48,9 @@
#include <net/if_bridgevar.h>
#include <net/route.h>
+#include <netlink/netlink_snl.h>
+#include <netlink/netlink_snl_generic.h>
+
#include <ctype.h>
#include <stdio.h>
#include <string.h>
@@ -64,6 +67,12 @@
static const char *stpproto[] = { STP_PROTOS };
static const char *stproles[] = { STP_ROLES };
+typedef struct net_link {
+ struct snl_state nl_state;
+ struct snl_writer nl_writer;
+ int nl_family_id;
+} net_link_t;
+
static int
get_val(const char *cp, u_long *valp)
{
@@ -273,26 +282,91 @@
ifconfig_bridge_free_bridge_status(bridge);
}
+static net_link_t *
+bridge_init_netlink(void)
+{
+ static net_link_t *nl = NULL;
+
+ if (nl != NULL)
+ return (nl);
+
+ if ((nl = calloc(1, sizeof(*nl))) == NULL)
+ err(1, "calloc");
+
+ if (!snl_init(&nl->nl_state, NETLINK_GENERIC))
+ errx(1, "snl_init() failed (netlink not supported?)");
+
+ nl->nl_family_id = snl_get_genl_family(&nl->nl_state,
+ BRIDGE_NL_FAMILY_NAME);
+ if (nl->nl_family_id == 0)
+ errx(1, "bridge netlink family not supported");
+
+ return (nl);
+}
+
+static struct nlmsghdr *
+bridge_prepare_netlink(net_link_t *nl, if_ctx *ctx, int request)
+{
+ struct nlmsghdr *hdr;
+
+ snl_init_writer(&nl->nl_state, &nl->nl_writer);
+ hdr = snl_create_genl_msg_request(&nl->nl_writer, nl->nl_family_id,
+ request);
+ snl_add_msg_attr_string(&nl->nl_writer, BRIDGE_NL_BRIDGE, ctx->ifname);
+
+ return (hdr);
+}
+
+static void
+bridge_send_netlink(net_link_t *nl, struct nlmsghdr *hdr)
+{
+ struct snl_errmsg_data e = { };
+ uint32_t seq_id;
+
+ hdr = snl_finalize_msg(&nl->nl_writer);
+ if (hdr == NULL)
+ errx(1, "failed to finalise the netlink message");
+
+ seq_id = hdr->nlmsg_seq;
+ if (!snl_send_message(&nl->nl_state, hdr))
+ errx(1, "failed to send the netlink message");
+
+ if (!snl_read_reply_code(&nl->nl_state, seq_id, &e)) {
+ if (e.error_str != NULL)
+ errx(1, "%s", e.error_str);
+ else if (e.error != 0)
+ errc(1, e.error, NULL);
+ else
+ errx(1, "failed to read netlink reply");
+ }
+}
+
static void
setbridge_add(if_ctx *ctx, const char *val, int dummy __unused)
{
- struct ifbreq req;
+ net_link_t *nl;
+ struct nlmsghdr *hdr;
- memset(&req, 0, sizeof(req));
- strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname));
- if (do_cmd(ctx, BRDGADD, &req, sizeof(req), 1) < 0)
- err(1, "BRDGADD %s", val);
+ nl = bridge_init_netlink();
+ hdr = bridge_prepare_netlink(nl, ctx, BRIDGE_NL_CMD_ADDMEMBER);
+
+ snl_add_msg_attr_string(&nl->nl_writer, BRIDGE_NL_MEMBER, val);
+
+ bridge_send_netlink(nl, hdr);
}
static void
setbridge_delete(if_ctx *ctx, const char *val, int dummy __unused)
{
- struct ifbreq req;
+ net_link_t *nl;
+ struct nlmsghdr *hdr;
- memset(&req, 0, sizeof(req));
- strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname));
- if (do_cmd(ctx, BRDGDEL, &req, sizeof(req), 1) < 0)
- err(1, "BRDGDEL %s", val);
+ nl = bridge_init_netlink();
+ hdr = bridge_prepare_netlink(nl, ctx, BRIDGE_NL_CMD_REMOVEMEMBER);
+
+ snl_add_msg_attr_string(&nl->nl_writer, BRIDGE_NL_MEMBER, val);
+
+ bridge_send_netlink(nl, hdr);
}
static void
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -132,6 +132,16 @@
#include <net/if_llc.h>
#include <net/if_vlan_var.h>
+#include <netlink/netlink.h>
+#include <netlink/netlink_ctl.h>
+#include <netlink/netlink_generic.h>
+#include <netlink/netlink_message_writer.h>
+
+#define DEBUG_MOD_NAME nl_bridge
+#define DEBUG_MAX_LEVEL LOG_DEBUG3
+#include <netlink/netlink_debug.h>
+_DECLARE_DEBUG(LOG_DEBUG);
+
#include <net/route.h>
#define EXTERR_CATEGORY EXTERR_CAT_BRIDGE
@@ -386,6 +396,9 @@
static void bridge_delete_span(struct bridge_softc *,
struct bridge_iflist *);
+static struct bridge_iflist *bridge_cmd_add(struct bridge_softc *sc,
+ const char *ifsname, int *error, char const **errmsg);
+
static int bridge_ioctl_add(struct bridge_softc *, void *);
static int bridge_ioctl_del(struct bridge_softc *, void *);
static int bridge_ioctl_gifflags(struct bridge_softc *, void *);
@@ -437,6 +450,9 @@
#endif /* INET6 */
static void bridge_linkstate(struct ifnet *ifp);
static void bridge_linkcheck(struct bridge_softc *sc);
+/* Netlink */
+static void bridge_nl_register(void);
+static void bridge_nl_unregister(void);
/*
* Use the "null" value from IEEE 802.1Q-2014 Table 9-2
@@ -724,6 +740,7 @@
bridge_detach_cookie = EVENTHANDLER_REGISTER(
ifnet_departure_event, bridge_ifdetach, NULL,
EVENTHANDLER_PRI_ANY);
+ bridge_nl_register();
break;
case MOD_UNLOAD:
EVENTHANDLER_DEREGISTER(ifnet_departure_event,
@@ -732,6 +749,7 @@
bridge_same_p = NULL;
bridge_get_softc_p = NULL;
bridge_member_ifaddrs_p = NULL;
+ bridge_nl_unregister();
break;
default:
return (EOPNOTSUPP);
@@ -1343,33 +1361,43 @@
NET_EPOCH_CALL(bridge_delete_member_cb, &bif->bif_epoch_ctx);
}
-static int
-bridge_ioctl_add(struct bridge_softc *sc, void *arg)
+static struct bridge_iflist *
+bridge_cmd_add(struct bridge_softc *sc, const char *ifsname, int *error,
+ char const **errmsg)
{
- struct ifbreq *req = arg;
- struct bridge_iflist *bif = NULL;
+ struct bridge_iflist *bif;
struct ifnet *ifs;
- int error = 0;
- ifs = ifunit(req->ifbr_ifsname);
+#define ERRMSG(e, s) \
+ do { \
+ *error = e; \
+ *errmsg = s; \
+ if (ifs != NULL) \
+ if_rele(ifs); \
+ return (NULL); \
+ } while (0)
+
+ *error = 0;
+
+ ifs = ifunit_ref(ifsname);
if (ifs == NULL)
- return (EXTERROR(ENOENT, "No such interface"));
+ ERRMSG(ENOENT, "No such interface");
+
if (ifs->if_ioctl == NULL) /* must be supported */
- return (EXTERROR(EINVAL, "Interface must support ioctl(2)"));
+ ERRMSG(EINVAL, "Interface must support ioctl(2)");
/* If it's in the span list, it can't be a member. */
CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next)
if (ifs == bif->bif_ifp)
- return (EXTERROR(EBUSY,
- "Span interface cannot be a member"));
+ ERRMSG(EBUSY, "Span interface cannot be a member");
if (ifs->if_bridge) {
struct bridge_iflist *sbif = ifs->if_bridge;
if (sbif->bif_sc == sc)
- return (EXTERROR(EEXIST,
- "Interface is already a member of this bridge"));
+ ERRMSG(EEXIST,
+ "Interface is already a member of this bridge");
- return EXTERROR(EBUSY,
+ ERRMSG(EBUSY,
"Interface is already a member of another bridge");
}
@@ -1380,7 +1408,7 @@
/* permitted interface types */
break;
default:
- return (EXTERROR(EINVAL, "Unsupported interface type"));
+ ERRMSG(EINVAL, "Unsupported interface type");
}
#ifdef INET6
@@ -1432,15 +1460,15 @@
CK_STAILQ_FOREACH(ifa, &ifs->if_addrhead, ifa_link) {
#ifdef INET
if (ifa->ifa_addr->sa_family == AF_INET)
- return (EXTERROR(EINVAL,
+ ERRMSG(EINVAL,
"Member interface may not have "
- "an IPv4 address configured"));
+ "an IPv4 address configured");
#endif
#ifdef INET6
if (ifa->ifa_addr->sa_family == AF_INET6)
- return (EXTERROR(EINVAL,
+ ERRMSG(EINVAL,
"Member interface may not have "
- "an IPv6 address configured"));
+ "an IPv6 address configured");
#endif
}
}
@@ -1455,21 +1483,19 @@
ifs->if_xname);
ifr.ifr_mtu = sc->sc_ifp->if_mtu;
- error = (*ifs->if_ioctl)(ifs,
- SIOCSIFMTU, (caddr_t)&ifr);
- if (error != 0) {
+ *error = (*ifs->if_ioctl)(ifs, SIOCSIFMTU, (caddr_t)&ifr);
+ if (*error != 0) {
log(LOG_NOTICE, "%s: invalid MTU: %u for"
" new member %s\n", sc->sc_ifp->if_xname,
ifr.ifr_mtu,
ifs->if_xname);
- return (EXTERROR(EINVAL,
- "Failed to set MTU on new member"));
+ ERRMSG(EINVAL, "Failed to set MTU on new member");
}
}
bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO);
if (bif == NULL)
- return (ENOMEM);
+ ERRMSG(ENOMEM, "Failed to allocate bridge member");
bif->bif_sc = sc;
bif->bif_ifp = ifs;
@@ -1513,13 +1539,33 @@
switch (ifs->if_type) {
case IFT_ETHER:
case IFT_L2VLAN:
- error = ifpromisc(ifs, 1);
+ *error = ifpromisc(ifs, 1);
break;
}
- if (error)
+ if (*error) {
bridge_delete_member(sc, bif, 0);
- return (error);
+ if_rele(ifs);
+ return (NULL);
+ }
+
+ if_rele(ifs);
+ return (bif);
+#undef ERRMSG
+}
+
+static int
+bridge_ioctl_add(struct bridge_softc *sc, void *arg)
+{
+ struct ifbreq *req = arg;
+ const char *errmsg;
+ int error = 0;
+
+ bridge_cmd_add(sc, req->ifbr_ifsname, &error, &errmsg);
+ if (error != 0)
+ return (EXTERROR(error, errmsg));
+
+ return (0);
}
static int
@@ -4430,3 +4476,182 @@
}
if_link_state_change(sc->sc_ifp, new_link);
}
+
+/*
+ * Netlink support.
+ */
+
+#define ERROUT(e) do { error = e; goto out; } while (0)
+#define ERRMSG(e, ...) \
+ do { \
+ nlmsg_report_err_msg(npt, __VA_ARGS__); \
+ ERROUT(e); \
+ } while (0)
+
+/* modifymember is used for both add and modify requests */
+
+struct brnl_modifymember {
+ const char *bridge;
+ const char *member;
+};
+
+#define _OUT(_field) offsetof(struct brnl_modifymember, _field)
+static const struct nlattr_parser nla_p_modifymember[] = {
+ { .type = BRIDGE_NL_BRIDGE, .off = _OUT(bridge), .cb = nlattr_get_string },
+ { .type = BRIDGE_NL_MEMBER, .off = _OUT(member), .cb = nlattr_get_string },
+};
+#undef _OUT
+NL_DECLARE_PARSER(modifymember_parser, struct genlmsghdr, nlf_p_empty,
+ nla_p_modifymember);
+
+struct brnl_removemember {
+ const char *bridge;
+ const char *member;
+};
+
+#define _OUT(_field) offsetof(struct brnl_removemember, _field)
+static const struct nlattr_parser nla_p_removemember[] = {
+ { .type = BRIDGE_NL_BRIDGE, .off = _OUT(bridge), .cb = nlattr_get_string },
+ { .type = BRIDGE_NL_MEMBER, .off = _OUT(member), .cb = nlattr_get_string },
+};
+#undef _OUT
+NL_DECLARE_PARSER(removemember_parser, struct genlmsghdr, nlf_p_empty,
+ nla_p_removemember);
+
+static const struct nlhdr_parser *bridge_parsers[] = {
+ &modifymember_parser,
+ &removemember_parser,
+};
+
+static int
+bridge_nl_addmember(struct nlmsghdr *hdr, struct nl_pstate *npt)
+{
+ struct brnl_modifymember attrs = {};
+ struct bridge_softc *sc;
+ struct ifnet *bridge;
+ const char *errmsg;
+ int error;
+
+ error = nl_parse_nlmsg(hdr, &modifymember_parser, npt, &attrs);
+ if (error != 0)
+ return (error);
+
+ /* Get the bridge ifnet */
+
+ sc = NULL;
+
+ bridge = ifunit_ref(attrs.bridge);
+ if (bridge == NULL)
+ ERRMSG(ENOENT, "Interface %s does not exist", attrs.bridge);
+
+ if (bridge->if_type != IFT_BRIDGE)
+ ERRMSG(EINVAL, "Interface %s is not a bridge",
+ bridge->if_xname);
+
+ sc = bridge->if_softc;
+ BRIDGE_LOCK(sc);
+
+ bridge_cmd_add(sc, attrs.member, &error, &errmsg);
+ if (error != 0)
+ ERRMSG(error, "%s", errmsg);
+
+out:
+ if (sc != NULL)
+ BRIDGE_UNLOCK(sc);
+ if (bridge != NULL)
+ if_rele(bridge);
+
+ return (error);
+}
+
+static int
+bridge_nl_removemember(struct nlmsghdr *hdr, struct nl_pstate *npt)
+{
+ struct brnl_removemember attrs = {};
+ struct bridge_softc *sc;
+ struct ifnet *bridge, *member;
+ struct bridge_iflist *bif;
+ int error;
+
+ error = nl_parse_nlmsg(hdr, &removemember_parser, npt, &attrs);
+ if (error != 0)
+ return (error);
+
+ /* Get the bridge ifnet */
+
+ sc = NULL;
+ member = NULL;
+
+ bridge = ifunit_ref(attrs.bridge);
+
+ if (bridge == NULL)
+ ERRMSG(ENOENT, "Interface %s does not exist", attrs.bridge);
+
+ if (bridge->if_type != IFT_BRIDGE)
+ ERRMSG(EINVAL, "Interface %s is not a bridge",
+ bridge->if_xname);
+
+ sc = bridge->if_softc;
+ BRIDGE_LOCK(sc);
+
+ /* Get the member bridge_iflist */
+ member = ifunit_ref(attrs.member);
+ if (member == NULL)
+ ERRMSG(ENOENT, "Interface %s does not exist", attrs.member);
+
+ bif = member->if_bridge;
+ if (bif == NULL)
+ ERRMSG(ENOENT, "Interface %s is not in a bridge",
+ member->if_xname);
+
+ if (bif->bif_sc != sc)
+ ERRMSG(ENOENT, "Interface %s is not in bridge %s",
+ member->if_xname, bridge->if_xname);
+
+ /* Delete the member */
+ bridge_delete_member(sc, bif, 0);
+
+out:
+ if (sc != NULL)
+ BRIDGE_UNLOCK(sc);
+ if (bridge != NULL)
+ if_rele(bridge);
+ if (member != NULL)
+ if_rele(member);
+ return error;
+}
+
+static const struct genl_cmd bridge_cmds[] = {
+ {
+ .cmd_num = BRIDGE_NL_CMD_ADDMEMBER,
+ .cmd_name = "ADDMEMBER",
+ .cmd_cb = bridge_nl_addmember,
+ .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
+ .cmd_priv = PRIV_NET_BRIDGE,
+ },
+ {
+ .cmd_num = BRIDGE_NL_CMD_REMOVEMEMBER,
+ .cmd_name = "REMOVEMEMBER",
+ .cmd_cb = bridge_nl_removemember,
+ .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
+ .cmd_priv = PRIV_NET_BRIDGE,
+ },
+};
+
+static uint16_t bridge_family_id;
+
+void
+bridge_nl_register(void)
+{
+ NL_VERIFY_PARSERS(bridge_parsers);
+
+ bridge_family_id = genl_register_family(BRIDGE_NL_FAMILY_NAME, 0, 2,
+ BRIDGE_NL_CMD_MAX);
+ genl_register_cmds(bridge_family_id, bridge_cmds, nitems(bridge_cmds));
+}
+
+void
+bridge_nl_unregister(void)
+{
+ genl_unregister_family(bridge_family_id);
+}
diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h
--- a/sys/net/if_bridgevar.h
+++ b/sys/net/if_bridgevar.h
@@ -87,12 +87,48 @@
#include <net/ethernet.h>
#include <net/if.h>
+/*
+ * Netlink interface.
+ */
+
+#define BRIDGE_NL_FAMILY_NAME "if_bridge"
+
+/*
+ * Attributes types
+ */
+#define BRIDGE_NL_BRIDGE 1 /* (string) bridge interface */
+#define BRIDGE_NL_MEMBER 2 /* (string) member interface */
+
+/*
+ * Commands.
+ */
+enum {
+ BRIDGE_NL_CMD_UNSPEC = 0,
+
+ /*
+ * BRIDGE_NL_CMD_ADDMEMBER: Add a new interface to the bridge.
+ * Arguments: BRIDGE_NL_BRIDGE: the bridge to add the member to
+ * BRIDGE_NL_MEMBER: the interface to add
+ */
+ BRIDGE_NL_CMD_ADDMEMBER,
+
+ /*
+ * BRIDGE_NL_CMD_REMOVEMEMBER: Remove an interface from a bridge.
+ * Arguments: BRIDGE_NL_BRIDGE: the bridge to remove the member from
+ * BRIDGE_NL_MEMBER: the interface to remove
+ */
+ BRIDGE_NL_CMD_REMOVEMEMBER,
+
+ __BRIDGE_NL_CMD_LAST
+};
+#define BRIDGE_NL_CMD_MAX (__BRIDGE_NL_CMD_LAST - 1)
+
/*
* Commands used in the SIOCSDRVSPEC ioctl. Note the lookup of the
* bridge interface itself is keyed off the ifdrv structure.
*/
-#define BRDGADD 0 /* add bridge member (ifbreq) */
-#define BRDGDEL 1 /* delete bridge member (ifbreq) */
+#define BRDGADD 0 /* compat; use Netlink instead */
+#define BRDGDEL 1 /* compat; use Netlink instead */
#define BRDGGIFFLGS 2 /* get member if flags (ifbreq) */
#define BRDGSIFFLGS 3 /* set member if flags (ifbreq) */
#define BRDGSCACHE 4 /* set cache size (ifbrparam) */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Feb 21, 7:57 AM (17 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28915321
Default Alt Text
D51238.diff (14 KB)
Attached To
Mode
D51238: bridge: add a netlink interface
Attached
Detach File
Event Timeline
Log In to Comment