Page MenuHomeFreeBSD

D38940.id119019.diff
No OneTemporary

D38940.id119019.diff

diff --git a/lib/libifconfig/libifconfig.h b/lib/libifconfig/libifconfig.h
--- a/lib/libifconfig/libifconfig.h
+++ b/lib/libifconfig/libifconfig.h
@@ -287,6 +287,8 @@
int32_t carpr_advbase;
int32_t carpr_advskew;
uint8_t carpr_key[CARP_KEY_LEN];
+ in_addr_t carpr_addr;
+ struct in6_addr carpr_addr6;
};
int ifconfig_carp_get_vhid(ifconfig_handle_t *h, const char *name,
diff --git a/lib/libifconfig/libifconfig_carp.c b/lib/libifconfig/libifconfig_carp.c
--- a/lib/libifconfig/libifconfig_carp.c
+++ b/lib/libifconfig/libifconfig_carp.c
@@ -55,6 +55,8 @@
{ .type = CARP_NL_ADVBASE, .off = _OUT(carpr_advbase), .cb = snl_attr_get_int32 },
{ .type = CARP_NL_ADVSKEW, .off = _OUT(carpr_advskew), .cb = snl_attr_get_int32 },
{ .type = CARP_NL_KEY, .off = _OUT(carpr_key), .cb = snl_attr_get_string },
+ { .type = CARP_NL_ADDR, .off = _OUT(carpr_addr), .cb = snl_attr_get_in_addr },
+ { .type = CARP_NL_ADDR6, .off = _OUT(carpr_addr6), .cb = snl_attr_get_in6_addr },
};
#undef _OUT
@@ -181,6 +183,10 @@
snl_add_msg_attr_s32(&nw, CARP_NL_ADVBASE, carpr->carpr_advbase);
snl_add_msg_attr_s32(&nw, CARP_NL_ADVSKEW, carpr->carpr_advskew);
snl_add_msg_attr_u32(&nw, CARP_NL_IFINDEX, ifindex);
+ snl_add_msg_attr(&nw, CARP_NL_ADDR, sizeof(carpr->carpr_addr),
+ &carpr->carpr_addr);
+ snl_add_msg_attr(&nw, CARP_NL_ADDR6, sizeof(carpr->carpr_addr6),
+ &carpr->carpr_addr6);
hdr = snl_finalize_msg(&nw);
if (hdr == NULL) {
diff --git a/sbin/ifconfig/carp.c b/sbin/ifconfig/carp.c
--- a/sbin/ifconfig/carp.c
+++ b/sbin/ifconfig/carp.c
@@ -42,13 +42,17 @@
#include <netinet/in_var.h>
#include <netinet/ip_carp.h>
+#include <arpa/inet.h>
+
#include <ctype.h>
+#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
+#include <netdb.h>
#include <libifconfig.h>
@@ -67,17 +71,22 @@
static int carpr_advskew = -1;
static int carpr_advbase = -1;
static int carpr_state = -1;
+static in_addr_t carp_addr;
+static struct in6_addr carp_addr6;
static unsigned char const *carpr_key;
static void
carp_status(int s)
{
struct ifconfig_carp carpr[CARP_MAXVHID];
+ char addr_buf[NI_MAXHOST];
if (ifconfig_carp_get_info(lifh, name, carpr, CARP_MAXVHID) == -1)
return;
for (size_t i = 0; i < carpr[0].carpr_count; i++) {
+ struct in_addr peer;
+
printf("\tcarp: %s vhid %d advbase %d advskew %d",
carp_states[carpr[i].carpr_state], carpr[i].carpr_vhid,
carpr[i].carpr_advbase, carpr[i].carpr_advskew);
@@ -85,6 +94,13 @@
printf(" key \"%s\"\n", carpr[i].carpr_key);
else
printf("\n");
+
+ peer.s_addr = carpr[i].carpr_addr;
+ inet_ntop(AF_INET6, &carpr[i].carpr_addr6, addr_buf,
+ sizeof(addr_buf));
+
+ printf("\t peer %s peer6 %s\n", inet_ntoa(peer),
+ addr_buf);
}
}
@@ -146,6 +162,11 @@
carpr.carpr_advbase = carpr_advbase;
if (carpr_state > -1)
carpr.carpr_state = carpr_state;
+ if (carp_addr != INADDR_ANY)
+ carpr.carpr_addr = carp_addr;
+ if (! IN6_IS_ADDR_UNSPECIFIED(&carp_addr6))
+ memcpy(&carpr.carpr_addr6, &carp_addr6,
+ sizeof(carp_addr6));
if (ifconfig_carp_set_info(lifh, name, &carpr))
err(1, "SIOCSVH");
@@ -198,12 +219,53 @@
errx(1, "unknown state");
}
+static void
+setcarp_peer(const char *val, int d, int s, const struct afswtch *afp)
+{
+ carp_addr = inet_addr(val);
+}
+
+static void
+setcarp_mcast(const char *val, int d, int s, const struct afswtch *afp)
+{
+ carp_addr = htonl(INADDR_CARP_GROUP);
+}
+
+static void
+setcarp_peer6(const char *val, int d, int s, const struct afswtch *afp)
+{
+ struct addrinfo hints, *res;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ if (getaddrinfo(val, NULL, &hints, &res) == 1)
+ errx(1, "Invalid IPv6 address %s", val);
+
+ memcpy(&carp_addr6, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
+ sizeof(carp_addr6));
+}
+
+static void
+setcarp_mcast6(const char *val, int d, int s, const struct afswtch *afp)
+{
+ bzero(&carp_addr6, sizeof(carp_addr6));
+ carp_addr6.s6_addr[0] = 0xff;
+ carp_addr6.s6_addr[1] = 0x02;
+ carp_addr6.s6_addr[15] = 0x12;
+}
+
static struct cmd carp_cmds[] = {
DEF_CMD_ARG("advbase", setcarp_advbase),
DEF_CMD_ARG("advskew", setcarp_advskew),
DEF_CMD_ARG("pass", setcarp_passwd),
DEF_CMD_ARG("vhid", setcarp_vhid),
DEF_CMD_ARG("state", setcarp_state),
+ DEF_CMD_ARG("peer", setcarp_peer),
+ DEF_CMD_ARG("mcast", setcarp_mcast),
+ DEF_CMD_ARG("peer6", setcarp_peer6),
+ DEF_CMD_ARG("mcast6", setcarp_mcast6),
};
static struct afswtch af_carp = {
.af_name = "af_carp",
@@ -216,6 +278,10 @@
{
int i;
+ /* Default to multicast. */
+ setcarp_mcast(NULL, 0, 0, NULL);
+ setcarp_mcast6(NULL, 0, 0, NULL);
+
for (i = 0; i < nitems(carp_cmds); i++)
cmd_register(&carp_cmds[i]);
af_register(&af_carp);
diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c
--- a/sys/netinet/ip_carp.c
+++ b/sys/netinet/ip_carp.c
@@ -111,6 +111,8 @@
int sc_vhid;
int sc_advskew;
int sc_advbase;
+ in_addr_t sc_carpaddr;
+ struct in6_addr sc_carpaddr6;
int sc_naddrs;
int sc_naddrs6;
@@ -155,6 +157,20 @@
#define CIF_PROMISC 0x00000001
};
+/* Kernel equivalent of struct carpreq, but with more fields for new features.
+ * */
+struct carpkreq {
+ int carpr_count;
+ int carpr_vhid;
+ int carpr_state;
+ int carpr_advskew;
+ int carpr_advbase;
+ unsigned char carpr_key[CARP_KEY_LEN];
+ /* Everything above this is identical to carpreq */
+ in_addr_t carpr_addr;
+ struct in6_addr carpr_addr6;
+};
+
/*
* Brief design of carp(4).
*
@@ -311,7 +327,7 @@
(((sc)->sc_advskew + V_carp_demotion < 0) ? \
0 : ((sc)->sc_advskew + V_carp_demotion)))
-static void carp_input_c(struct mbuf *, struct carp_header *, sa_family_t);
+static void carp_input_c(struct mbuf *, struct carp_header *, sa_family_t, int);
static struct carp_softc
*carp_alloc(struct ifnet *);
static void carp_destroy(struct carp_softc *);
@@ -471,16 +487,6 @@
return (IPPROTO_DONE);
}
- /* verify that the IP TTL is 255. */
- if (ip->ip_ttl != CARP_DFLTTL) {
- CARPSTATS_INC(carps_badttl);
- CARP_DEBUG("%s: received ttl %d != 255 on %s\n", __func__,
- ip->ip_ttl,
- m->m_pkthdr.rcvif->if_xname);
- m_freem(m);
- return (IPPROTO_DONE);
- }
-
iplen = ip->ip_hl << 2;
if (m->m_pkthdr.len < iplen + sizeof(*ch)) {
@@ -534,7 +540,7 @@
}
m->m_data -= iplen;
- carp_input_c(m, ch, AF_INET);
+ carp_input_c(m, ch, AF_INET, ip->ip_ttl);
return (IPPROTO_DONE);
}
#endif
@@ -564,15 +570,6 @@
return (IPPROTO_DONE);
}
- /* verify that the IP TTL is 255 */
- if (ip6->ip6_hlim != CARP_DFLTTL) {
- CARPSTATS_INC(carps_badttl);
- CARP_DEBUG("%s: received ttl %d != 255 on %s\n", __func__,
- ip6->ip6_hlim, m->m_pkthdr.rcvif->if_xname);
- m_freem(m);
- return (IPPROTO_DONE);
- }
-
/* verify that we have a complete carp packet */
if (m->m_len < *offp + sizeof(*ch)) {
len = m->m_len;
@@ -582,6 +579,7 @@
CARP_DEBUG("%s: packet size %u too small\n", __func__, len);
return (IPPROTO_DONE);
}
+ ip6 = mtod(m, struct ip6_hdr *);
}
ch = (struct carp_header *)(mtod(m, char *) + *offp);
@@ -596,7 +594,7 @@
}
m->m_data -= *offp;
- carp_input_c(m, ch, AF_INET6);
+ carp_input_c(m, ch, AF_INET6, ip6->ip6_hlim);
return (IPPROTO_DONE);
}
#endif /* INET6 */
@@ -647,7 +645,7 @@
}
static void
-carp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af)
+carp_input_c(struct mbuf *m, struct carp_header *ch, sa_family_t af, int ttl)
{
struct ifnet *ifp = m->m_pkthdr.rcvif;
struct ifaddr *ifa, *match;
@@ -655,6 +653,7 @@
uint64_t tmp_counter;
struct timeval sc_tv, ch_tv;
int error;
+ bool multicast = false;
NET_EPOCH_ASSERT();
@@ -707,8 +706,21 @@
sc = ifa->ifa_carp;
CARP_LOCK(sc);
+ if (ifa->ifa_addr->sa_family == AF_INET) {
+ multicast = IN_MULTICAST(sc->sc_carpaddr);
+ } else {
+ multicast = IN6_IS_ADDR_MULTICAST(&sc->sc_carpaddr6);
+ }
ifa_free(ifa);
+ /* verify that the IP TTL is 255, but only if we're not in unicast mode. */
+ if (multicast && ttl != CARP_DFLTTL) {
+ CARPSTATS_INC(carps_badttl);
+ CARP_DEBUG("%s: received ttl %d != 255 on %s\n", __func__,
+ ttl, m->m_pkthdr.rcvif->if_xname);
+ goto out;
+ }
+
if (carp_hmac_verify(sc, ch->carp_counter, ch->carp_md)) {
CARPSTATS_INC(carps_badauth);
CARP_DEBUG("%s: incorrect hash for VHID %u@%s\n", __func__,
@@ -961,7 +973,8 @@
m->m_pkthdr.rcvif = NULL;
m->m_len = len;
M_ALIGN(m, m->m_len);
- m->m_flags |= M_MCAST;
+ if (IN_MULTICAST(sc->sc_carpaddr))
+ m->m_flags |= M_MCAST;
ip = mtod(m, struct ip *);
ip->ip_v = IPVERSION;
ip->ip_hl = sizeof(*ip) >> 2;
@@ -980,7 +993,7 @@
ifa_free(ifa);
} else
ip->ip_src.s_addr = 0;
- ip->ip_dst.s_addr = htonl(INADDR_CARP_GROUP);
+ ip->ip_dst.s_addr = sc->sc_carpaddr;
ch_ptr = (struct carp_header *)(&ip[1]);
bcopy(&ch, ch_ptr, sizeof(ch));
@@ -1011,7 +1024,6 @@
m->m_pkthdr.rcvif = NULL;
m->m_len = len;
M_ALIGN(m, m->m_len);
- m->m_flags |= M_MCAST;
ip6 = mtod(m, struct ip6_hdr *);
bzero(ip6, sizeof(*ip6));
ip6->ip6_vfc |= IPV6_VERSION;
@@ -1033,12 +1045,13 @@
bzero(&ip6->ip6_src, sizeof(struct in6_addr));
/* Set the multicast destination. */
- ip6->ip6_dst.s6_addr16[0] = htons(0xff02);
- ip6->ip6_dst.s6_addr8[15] = 0x12;
- if (in6_setscope(&ip6->ip6_dst, sc->sc_carpdev, NULL) != 0) {
- m_freem(m);
- CARP_DEBUG("%s: in6_setscope failed\n", __func__);
- goto resched;
+ memcpy(&ip6->ip6_dst, &sc->sc_carpaddr6, sizeof(ip6->ip6_dst));
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ if (in6_setscope(&ip6->ip6_dst, sc->sc_carpdev, NULL) != 0) {
+ m_freem(m);
+ CARP_DEBUG("%s: in6_setscope failed\n", __func__);
+ goto resched;
+ }
}
ch_ptr = (struct carp_header *)(&ip6[1]);
@@ -1554,6 +1567,19 @@
bcopy(mtag + 1, &sc, sizeof(sc));
+ switch (sa->sa_family) {
+ case AF_INET:
+ if (! IN_MULTICAST(sc->sc_carpaddr))
+ return (0);
+ break;
+ case AF_INET6:
+ if (! IN6_IS_ADDR_MULTICAST(&sc->sc_carpaddr6))
+ return (0);
+ break;
+ default:
+ panic("Unknown af");
+ }
+
/* Set the source MAC address to the Virtual Router MAC Address. */
switch (ifp->if_type) {
case IFT_ETHER:
@@ -1601,6 +1627,10 @@
sc->sc_ifas = malloc(sc->sc_ifasiz, M_CARP, M_WAITOK|M_ZERO);
sc->sc_carpdev = ifp;
+ sc->sc_carpaddr = htonl(INADDR_CARP_GROUP);
+ sc->sc_carpaddr6.s6_addr16[0] = htons(0xff02);
+ sc->sc_carpaddr6.s6_addr8[15] = 0x12;
+
CARP_LOCK_INIT(sc);
#ifdef INET
callout_init_mtx(&sc->sc_md_tmo, &sc->sc_mtx, CALLOUT_RETURNUNLOCKED);
@@ -1736,7 +1766,7 @@
}
static int
-carp_ioctl_set(if_t ifp, struct ucred *cred, struct carpreq *carpr)
+carp_ioctl_set(if_t ifp, struct ucred *cred, struct carpkreq *carpr)
{
struct epoch_tracker et;
struct carp_softc *sc = NULL;
@@ -1778,6 +1808,12 @@
goto out;
}
sc->sc_advskew = carpr->carpr_advskew;
+ if (carpr->carpr_addr != INADDR_ANY)
+ sc->sc_carpaddr = carpr->carpr_addr;
+ if (! IN6_IS_ADDR_UNSPECIFIED(&carpr->carpr_addr6)) {
+ memcpy(&sc->sc_carpaddr6, &carpr->carpr_addr6,
+ sizeof(sc->sc_carpaddr6));
+ }
if (carpr->carpr_key[0] != '\0') {
bcopy(carpr->carpr_key, sc->sc_key, sizeof(sc->sc_key));
carp_hmac_prepare(sc);
@@ -1858,6 +1894,7 @@
carp_ioctl(struct ifreq *ifr, u_long cmd, struct thread *td)
{
struct carpreq carpr;
+ struct carpkreq carprk;
struct ifnet *ifp;
int error = 0;
@@ -1886,7 +1923,9 @@
sx_xlock(&carp_sx);
switch (cmd) {
case SIOCSVH:
- error = carp_ioctl_set(ifp, td->td_ucred, &carpr);
+ memset(&carprk, 0, sizeof(carprk));
+ memcpy(&carprk, &carpr, sizeof(carpr));
+ error = carp_ioctl_set(ifp, td->td_ucred, &carprk);
break;
case SIOCGVH:
@@ -2250,6 +2289,8 @@
nlattr_add_u32(nw, CARP_NL_STATE, sc->sc_state);
nlattr_add_s32(nw, CARP_NL_ADVBASE, sc->sc_advbase);
nlattr_add_s32(nw, CARP_NL_ADVSKEW, sc->sc_advskew);
+ nlattr_add_in_addr(nw, CARP_NL_ADDR, &sc->sc_carpaddr);
+ nlattr_add_in6_addr(nw, CARP_NL_ADDR6, &sc->sc_carpaddr6);
if (priv)
nlattr_add(nw, CARP_NL_KEY, sizeof(sc->sc_key), sc->sc_key);
@@ -2321,6 +2362,8 @@
int32_t advbase;
int32_t advskew;
char key[CARP_KEY_LEN];
+ in_addr_t addr;
+ struct in6_addr addr6;
};
#define _IN(_field) offsetof(struct genlmsghdr, _field)
@@ -2333,6 +2376,8 @@
{ .type = CARP_NL_ADVSKEW, .off = _OUT(advskew), .cb = nlattr_get_uint32 },
{ .type = CARP_NL_KEY, .off = _OUT(key), .cb = nlattr_get_carp_key },
{ .type = CARP_NL_IFINDEX, .off = _OUT(ifindex), .cb = nlattr_get_uint32 },
+ { .type = CARP_NL_ADDR, .off = _OUT(addr), .cb = nlattr_get_in_addr },
+ { .type = CARP_NL_ADDR6, .off = _OUT(addr6), .cb = nlattr_get_in6_addr },
};
static const struct nlfield_parser nlf_p_set[] = {
};
@@ -2344,7 +2389,7 @@
carp_nl_set(struct nlmsghdr *hdr, struct nl_pstate *npt)
{
struct nl_carp_parsed_set attrs = { };
- struct carpreq carpr;
+ struct carpkreq carpr;
struct epoch_tracker et;
if_t ifp;
int error;
@@ -2390,6 +2435,9 @@
carpr.carpr_state = attrs.state;
carpr.carpr_advbase = attrs.advbase;
carpr.carpr_advskew = attrs.advskew;
+ carpr.carpr_addr = attrs.addr;
+ carpr.carpr_addr6 = attrs.addr6;
+
memcpy(&carpr.carpr_key, &attrs.key, sizeof(attrs.key));
sx_xlock(&carp_sx);
diff --git a/sys/netinet/ip_carp_nl.h b/sys/netinet/ip_carp_nl.h
--- a/sys/netinet/ip_carp_nl.h
+++ b/sys/netinet/ip_carp_nl.h
@@ -29,6 +29,8 @@
CARP_NL_ADVSKEW = 4, /* s32 */
CARP_NL_KEY = 5, /* byte array */
CARP_NL_IFINDEX = 6, /* u32 */
+ CARP_NL_ADDR = 7, /* in_addr_t */
+ CARP_NL_ADDR6 = 8, /* in6_addr_t */
};
#endif
diff --git a/sys/netlink/netlink_message_parser.h b/sys/netlink/netlink_message_parser.h
--- a/sys/netlink/netlink_message_parser.h
+++ b/sys/netlink/netlink_message_parser.h
@@ -32,6 +32,8 @@
#include <sys/bitset.h>
+#include <netinet/in.h>
+
/*
* It is not meant to be included directly
*/
@@ -175,6 +177,10 @@
const void *arg, void *target);
int nlattr_get_uint64(struct nlattr *nla, struct nl_pstate *npt,
const void *arg, void *target);
+int nlattr_get_in_addr(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
+int nlattr_get_in6_addr(struct nlattr *nla, struct nl_pstate *npt,
+ const void *arg, void *target);
int nlattr_get_ifp(struct nlattr *nla, struct nl_pstate *npt,
const void *arg, void *target);
int nlattr_get_ifpz(struct nlattr *nla, struct nl_pstate *npt,
diff --git a/sys/netlink/netlink_message_parser.c b/sys/netlink/netlink_message_parser.c
--- a/sys/netlink/netlink_message_parser.c
+++ b/sys/netlink/netlink_message_parser.c
@@ -348,6 +348,29 @@
memcpy(target, NL_RTA_DATA_CONST(nla), sizeof(uint64_t));
return (0);
}
+int
+nlattr_get_in_addr(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
+{
+ if (__predict_false(NLA_DATA_LEN(nla) != sizeof(in_addr_t))) {
+ NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not in_addr_t",
+ nla->nla_type, NLA_DATA_LEN(nla));
+ return (EINVAL);
+ }
+ memcpy(target, NLA_DATA_CONST(nla), sizeof(in_addr_t));
+ return (0);
+}
+
+int
+nlattr_get_in6_addr(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
+{
+ if (__predict_false(NLA_DATA_LEN(nla) != sizeof(struct in6_addr))) {
+ NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not struct in6_addr",
+ nla->nla_type, NLA_DATA_LEN(nla));
+ return (EINVAL);
+ }
+ memcpy(target, NLA_DATA_CONST(nla), sizeof(struct in6_addr));
+ return (0);
+}
static int
nlattr_get_ifp_internal(struct nlattr *nla, struct nl_pstate *npt,
diff --git a/sys/netlink/netlink_message_writer.h b/sys/netlink/netlink_message_writer.h
--- a/sys/netlink/netlink_message_writer.h
+++ b/sys/netlink/netlink_message_writer.h
@@ -30,6 +30,9 @@
#define _NETLINK_NETLINK_MESSAGE_WRITER_H_
#ifdef _KERNEL
+
+#include <netinet/in.h>
+
/*
* It is not meant to be included directly
*/
@@ -248,6 +251,18 @@
return (nlattr_add(nw, attrtype, sizeof(int64_t), &value));
}
+static inline bool
+nlattr_add_in_addr(struct nl_writer *nw, int attrtype, const in_addr_t *in)
+{
+ return (nlattr_add(nw, attrtype, sizeof(*in), in));
+}
+
+static inline bool
+nlattr_add_in6_addr(struct nl_writer *nw, int attrtype, const struct in6_addr *in6)
+{
+ return (nlattr_add(nw, attrtype, sizeof(*in6), in6));
+}
+
static inline bool
nlattr_add_flag(struct nl_writer *nw, int attrtype)
{
diff --git a/sys/netlink/netlink_snl.h b/sys/netlink/netlink_snl.h
--- a/sys/netlink/netlink_snl.h
+++ b/sys/netlink/netlink_snl.h
@@ -44,6 +44,7 @@
#include <sys/socket.h>
#include <netlink/netlink.h>
+#include <netinet/in.h>
#define _roundup2(x, y) (((x)+((y)-1))&(~((y)-1)))
@@ -510,6 +511,28 @@
return (false);
}
+static inline bool
+snl_attr_get_in_addr(struct snl_state *ss __unused, struct nlattr *nla,
+ const void *arg __unused, void *target)
+{
+ if (NLA_DATA_LEN(nla) != sizeof(in_addr_t))
+ return (false);
+
+ memcpy(target, NLA_DATA_CONST(nla), sizeof(in_addr_t));
+ return (true);
+}
+
+static inline bool
+snl_attr_get_in6_addr(struct snl_state *ss __unused, struct nlattr *nla,
+ const void *arg __unused, void *target)
+{
+ if (NLA_DATA_LEN(nla) != sizeof(struct in6_addr))
+ return (false);
+
+ memcpy(target, NLA_DATA_CONST(nla), sizeof(struct in6_addr));
+ return (true);
+}
+
static inline bool
snl_attr_get_stringn(struct snl_state *ss, struct nlattr *nla,
const void *arg __unused, void *target)

File Metadata

Mime Type
text/plain
Expires
Wed, Apr 30, 12:50 AM (9 h, 48 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17848976
Default Alt Text
D38940.id119019.diff (17 KB)

Event Timeline