mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-05-29 21:01:42 +00:00
Merge pull request #5892 from qlyoung/fix-zclient-many
assorted lib / zclient fixes
This commit is contained in:
commit
1b0f1cb4d7
30
lib/if.c
30
lib/if.c
@ -582,23 +582,39 @@ struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void if_set_index(struct interface *ifp, ifindex_t ifindex)
|
int if_set_index(struct interface *ifp, ifindex_t ifindex)
|
||||||
{
|
{
|
||||||
struct vrf *vrf;
|
struct vrf *vrf;
|
||||||
|
|
||||||
|
if (ifp->ifindex == ifindex)
|
||||||
|
return 0;
|
||||||
|
|
||||||
vrf = vrf_get(ifp->vrf_id, NULL);
|
vrf = vrf_get(ifp->vrf_id, NULL);
|
||||||
assert(vrf);
|
assert(vrf);
|
||||||
|
|
||||||
if (ifp->ifindex == ifindex)
|
/*
|
||||||
return;
|
* If there is already an interface with this ifindex, we will collide
|
||||||
|
* on insertion, so don't even try.
|
||||||
|
*/
|
||||||
|
if (if_lookup_by_ifindex(ifindex, ifp->vrf_id))
|
||||||
|
return -1;
|
||||||
|
|
||||||
if (ifp->ifindex != IFINDEX_INTERNAL)
|
if (ifp->ifindex != IFINDEX_INTERNAL)
|
||||||
IFINDEX_RB_REMOVE(vrf, ifp);
|
IFINDEX_RB_REMOVE(vrf, ifp);
|
||||||
|
|
||||||
ifp->ifindex = ifindex;
|
ifp->ifindex = ifindex;
|
||||||
|
|
||||||
if (ifp->ifindex != IFINDEX_INTERNAL)
|
if (ifp->ifindex != IFINDEX_INTERNAL) {
|
||||||
IFINDEX_RB_INSERT(vrf, ifp)
|
/*
|
||||||
|
* This should never happen, since we checked if there was
|
||||||
|
* already an interface with the desired ifindex at the top of
|
||||||
|
* the function. Nevertheless.
|
||||||
|
*/
|
||||||
|
if (IFINDEX_RB_INSERT(vrf, ifp))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void if_set_name(struct interface *ifp, const char *name)
|
void if_set_name(struct interface *ifp, const char *name)
|
||||||
@ -1249,8 +1265,6 @@ struct if_link_params *if_link_params_get(struct interface *ifp)
|
|||||||
|
|
||||||
struct if_link_params *iflp =
|
struct if_link_params *iflp =
|
||||||
XCALLOC(MTYPE_IF_LINK_PARAMS, sizeof(struct if_link_params));
|
XCALLOC(MTYPE_IF_LINK_PARAMS, sizeof(struct if_link_params));
|
||||||
if (iflp == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* Set TE metric equal to standard metric */
|
/* Set TE metric equal to standard metric */
|
||||||
iflp->te_metric = ifp->metric;
|
iflp->te_metric = ifp->metric;
|
||||||
@ -1278,8 +1292,6 @@ struct if_link_params *if_link_params_get(struct interface *ifp)
|
|||||||
|
|
||||||
void if_link_params_free(struct interface *ifp)
|
void if_link_params_free(struct interface *ifp)
|
||||||
{
|
{
|
||||||
if (ifp->link_params == NULL)
|
|
||||||
return;
|
|
||||||
XFREE(MTYPE_IF_LINK_PARAMS, ifp->link_params);
|
XFREE(MTYPE_IF_LINK_PARAMS, ifp->link_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
75
lib/if.h
75
lib/if.h
@ -308,33 +308,58 @@ RB_HEAD(if_index_head, interface);
|
|||||||
RB_PROTOTYPE(if_index_head, interface, index_entry, if_cmp_index_func)
|
RB_PROTOTYPE(if_index_head, interface, index_entry, if_cmp_index_func)
|
||||||
DECLARE_QOBJ_TYPE(interface)
|
DECLARE_QOBJ_TYPE(interface)
|
||||||
|
|
||||||
#define IFNAME_RB_INSERT(vrf, ifp) \
|
#define IFNAME_RB_INSERT(vrf, ifp) \
|
||||||
if (RB_INSERT(if_name_head, &vrf->ifaces_by_name, (ifp))) \
|
({ \
|
||||||
flog_err(EC_LIB_INTERFACE, \
|
struct interface *_iz = \
|
||||||
"%s(%s): corruption detected -- interface with this " \
|
RB_INSERT(if_name_head, &vrf->ifaces_by_name, (ifp)); \
|
||||||
"name exists already in VRF %u!", \
|
if (_iz) \
|
||||||
__func__, (ifp)->name, (ifp)->vrf_id);
|
flog_err( \
|
||||||
|
EC_LIB_INTERFACE, \
|
||||||
|
"%s(%s): corruption detected -- interface with this " \
|
||||||
|
"name exists already in VRF %u!", \
|
||||||
|
__func__, (ifp)->name, (ifp)->vrf_id); \
|
||||||
|
_iz; \
|
||||||
|
})
|
||||||
|
|
||||||
#define IFNAME_RB_REMOVE(vrf, ifp) \
|
#define IFNAME_RB_REMOVE(vrf, ifp) \
|
||||||
if (RB_REMOVE(if_name_head, &vrf->ifaces_by_name, (ifp)) == NULL) \
|
({ \
|
||||||
flog_err(EC_LIB_INTERFACE, \
|
struct interface *_iz = \
|
||||||
"%s(%s): corruption detected -- interface with this " \
|
RB_REMOVE(if_name_head, &vrf->ifaces_by_name, (ifp)); \
|
||||||
"name doesn't exist in VRF %u!", \
|
if (_iz == NULL) \
|
||||||
__func__, (ifp)->name, (ifp)->vrf_id);
|
flog_err( \
|
||||||
|
EC_LIB_INTERFACE, \
|
||||||
|
"%s(%s): corruption detected -- interface with this " \
|
||||||
|
"name doesn't exist in VRF %u!", \
|
||||||
|
__func__, (ifp)->name, (ifp)->vrf_id); \
|
||||||
|
_iz; \
|
||||||
|
})
|
||||||
|
|
||||||
#define IFINDEX_RB_INSERT(vrf, ifp) \
|
|
||||||
if (RB_INSERT(if_index_head, &vrf->ifaces_by_index, (ifp))) \
|
|
||||||
flog_err(EC_LIB_INTERFACE, \
|
|
||||||
"%s(%u): corruption detected -- interface with this " \
|
|
||||||
"ifindex exists already in VRF %u!", \
|
|
||||||
__func__, (ifp)->ifindex, (ifp)->vrf_id);
|
|
||||||
|
|
||||||
#define IFINDEX_RB_REMOVE(vrf, ifp) \
|
#define IFINDEX_RB_INSERT(vrf, ifp) \
|
||||||
if (RB_REMOVE(if_index_head, &vrf->ifaces_by_index, (ifp)) == NULL) \
|
({ \
|
||||||
flog_err(EC_LIB_INTERFACE, \
|
struct interface *_iz = RB_INSERT( \
|
||||||
"%s(%u): corruption detected -- interface with this " \
|
if_index_head, &vrf->ifaces_by_index, (ifp)); \
|
||||||
"ifindex doesn't exist in VRF %u!", \
|
if (_iz) \
|
||||||
__func__, (ifp)->ifindex, (ifp)->vrf_id);
|
flog_err( \
|
||||||
|
EC_LIB_INTERFACE, \
|
||||||
|
"%s(%u): corruption detected -- interface with this " \
|
||||||
|
"ifindex exists already in VRF %u!", \
|
||||||
|
__func__, (ifp)->ifindex, (ifp)->vrf_id); \
|
||||||
|
_iz; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define IFINDEX_RB_REMOVE(vrf, ifp) \
|
||||||
|
({ \
|
||||||
|
struct interface *_iz = RB_REMOVE( \
|
||||||
|
if_index_head, &vrf->ifaces_by_index, (ifp)); \
|
||||||
|
if (_iz == NULL) \
|
||||||
|
flog_err( \
|
||||||
|
EC_LIB_INTERFACE, \
|
||||||
|
"%s(%u): corruption detected -- interface with this " \
|
||||||
|
"ifindex doesn't exist in VRF %u!", \
|
||||||
|
__func__, (ifp)->ifindex, (ifp)->vrf_id); \
|
||||||
|
_iz; \
|
||||||
|
})
|
||||||
|
|
||||||
#define FOR_ALL_INTERFACES(vrf, ifp) \
|
#define FOR_ALL_INTERFACES(vrf, ifp) \
|
||||||
if (vrf) \
|
if (vrf) \
|
||||||
@ -502,7 +527,7 @@ extern struct interface *if_get_by_name(const char *ifname, vrf_id_t vrf_id);
|
|||||||
extern struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id);
|
extern struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id);
|
||||||
|
|
||||||
/* Sets the index and adds to index list */
|
/* Sets the index and adds to index list */
|
||||||
extern void if_set_index(struct interface *ifp, ifindex_t ifindex);
|
extern int if_set_index(struct interface *ifp, ifindex_t ifindex);
|
||||||
/* Sets the name and adds to name list */
|
/* Sets the name and adds to name list */
|
||||||
extern void if_set_name(struct interface *ifp, const char *name);
|
extern void if_set_name(struct interface *ifp, const char *name);
|
||||||
|
|
||||||
|
21
lib/stream.c
21
lib/stream.c
@ -543,6 +543,27 @@ uint64_t stream_getq(struct stream *s)
|
|||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool stream_getq2(struct stream *s, uint64_t *q)
|
||||||
|
{
|
||||||
|
STREAM_VERIFY_SANE(s);
|
||||||
|
|
||||||
|
if (STREAM_READABLE(s) < sizeof(uint64_t)) {
|
||||||
|
STREAM_BOUND_WARN2(s, "get uint64");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*q = ((uint64_t)s->data[s->getp++]) << 56;
|
||||||
|
*q |= ((uint64_t)s->data[s->getp++]) << 48;
|
||||||
|
*q |= ((uint64_t)s->data[s->getp++]) << 40;
|
||||||
|
*q |= ((uint64_t)s->data[s->getp++]) << 32;
|
||||||
|
*q |= ((uint64_t)s->data[s->getp++]) << 24;
|
||||||
|
*q |= ((uint64_t)s->data[s->getp++]) << 16;
|
||||||
|
*q |= ((uint64_t)s->data[s->getp++]) << 8;
|
||||||
|
*q |= ((uint64_t)s->data[s->getp++]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get next long word from the stream. */
|
/* Get next long word from the stream. */
|
||||||
uint32_t stream_get_ipv4(struct stream *s)
|
uint32_t stream_get_ipv4(struct stream *s)
|
||||||
{
|
{
|
||||||
|
20
lib/stream.h
20
lib/stream.h
@ -215,6 +215,7 @@ extern bool stream_getl2(struct stream *s, uint32_t *l);
|
|||||||
extern uint32_t stream_getl_from(struct stream *, size_t);
|
extern uint32_t stream_getl_from(struct stream *, size_t);
|
||||||
extern uint64_t stream_getq(struct stream *);
|
extern uint64_t stream_getq(struct stream *);
|
||||||
extern uint64_t stream_getq_from(struct stream *, size_t);
|
extern uint64_t stream_getq_from(struct stream *, size_t);
|
||||||
|
bool stream_getq2(struct stream *s, uint64_t *q);
|
||||||
extern uint32_t stream_get_ipv4(struct stream *);
|
extern uint32_t stream_get_ipv4(struct stream *);
|
||||||
|
|
||||||
/* IEEE-754 floats */
|
/* IEEE-754 floats */
|
||||||
@ -402,6 +403,25 @@ static inline const uint8_t *ptr_get_be32(const uint8_t *ptr, uint32_t *out)
|
|||||||
(P) = _pval; \
|
(P) = _pval; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define STREAM_GETF(S, P) \
|
||||||
|
do { \
|
||||||
|
union { \
|
||||||
|
float r; \
|
||||||
|
uint32_t d; \
|
||||||
|
} _pval; \
|
||||||
|
if (stream_getl2((S), &_pval.d)) \
|
||||||
|
goto stream_failure; \
|
||||||
|
(P) = _pval.r; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define STREAM_GETQ(S, P) \
|
||||||
|
do { \
|
||||||
|
uint64_t _pval; \
|
||||||
|
if (!stream_getq2((S), &_pval)) \
|
||||||
|
goto stream_failure; \
|
||||||
|
(P) = _pval; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define STREAM_GET(P, STR, SIZE) \
|
#define STREAM_GET(P, STR, SIZE) \
|
||||||
do { \
|
do { \
|
||||||
if (!stream_get2((P), (STR), (SIZE))) \
|
if (!stream_get2((P), (STR), (SIZE))) \
|
||||||
|
16
lib/vrf.c
16
lib/vrf.c
@ -593,10 +593,22 @@ int vrf_get_backend(void)
|
|||||||
return vrf_backend;
|
return vrf_backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vrf_configure_backend(int vrf_backend_netns)
|
int vrf_configure_backend(enum vrf_backend_type backend)
|
||||||
{
|
{
|
||||||
vrf_backend = vrf_backend_netns;
|
/* Work around issue in old gcc */
|
||||||
|
switch (backend) {
|
||||||
|
case VRF_BACKEND_UNKNOWN:
|
||||||
|
case VRF_BACKEND_NETNS:
|
||||||
|
case VRF_BACKEND_VRF_LITE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vrf_backend = backend;
|
||||||
vrf_backend_configured = 1;
|
vrf_backend_configured = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int vrf_handler_create(struct vty *vty, const char *vrfname,
|
int vrf_handler_create(struct vty *vty, const char *vrfname,
|
||||||
|
15
lib/vrf.h
15
lib/vrf.h
@ -101,9 +101,12 @@ RB_PROTOTYPE(vrf_name_head, vrf, name_entry, vrf_name_compare)
|
|||||||
DECLARE_QOBJ_TYPE(vrf)
|
DECLARE_QOBJ_TYPE(vrf)
|
||||||
|
|
||||||
/* Allow VRF with netns as backend */
|
/* Allow VRF with netns as backend */
|
||||||
#define VRF_BACKEND_VRF_LITE 0
|
enum vrf_backend_type {
|
||||||
#define VRF_BACKEND_NETNS 1
|
VRF_BACKEND_VRF_LITE,
|
||||||
#define VRF_BACKEND_UNKNOWN 2
|
VRF_BACKEND_NETNS,
|
||||||
|
VRF_BACKEND_UNKNOWN,
|
||||||
|
VRF_BACKEND_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
extern struct vrf_id_head vrfs_by_id;
|
extern struct vrf_id_head vrfs_by_id;
|
||||||
extern struct vrf_name_head vrfs_by_name;
|
extern struct vrf_name_head vrfs_by_name;
|
||||||
@ -292,10 +295,10 @@ extern void vrf_install_commands(void);
|
|||||||
* VRF utilities
|
* VRF utilities
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* API for configuring VRF backend
|
/*
|
||||||
* should be called from zebra only
|
* API for configuring VRF backend
|
||||||
*/
|
*/
|
||||||
extern void vrf_configure_backend(int vrf_backend_netns);
|
extern int vrf_configure_backend(enum vrf_backend_type backend);
|
||||||
extern int vrf_get_backend(void);
|
extern int vrf_get_backend(void);
|
||||||
extern int vrf_is_backend_netns(void);
|
extern int vrf_is_backend_netns(void);
|
||||||
|
|
||||||
|
230
lib/zclient.c
230
lib/zclient.c
@ -1635,33 +1635,34 @@ int zebra_redistribute_default_send(int command, struct zclient *zclient,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Get prefix in ZServ format; family should be filled in on prefix */
|
/* Get prefix in ZServ format; family should be filled in on prefix */
|
||||||
static void zclient_stream_get_prefix(struct stream *s, struct prefix *p)
|
static int zclient_stream_get_prefix(struct stream *s, struct prefix *p)
|
||||||
{
|
{
|
||||||
size_t plen = prefix_blen(p);
|
size_t plen = prefix_blen(p);
|
||||||
uint8_t c;
|
uint8_t c;
|
||||||
p->prefixlen = 0;
|
p->prefixlen = 0;
|
||||||
|
|
||||||
if (plen == 0)
|
if (plen == 0)
|
||||||
return;
|
return -1;
|
||||||
|
|
||||||
stream_get(&p->u.prefix, s, plen);
|
STREAM_GET(&p->u.prefix, s, plen);
|
||||||
STREAM_GETC(s, c);
|
STREAM_GETC(s, c);
|
||||||
p->prefixlen = MIN(plen * 8, c);
|
p->prefixlen = MIN(plen * 8, c);
|
||||||
|
|
||||||
|
return 0;
|
||||||
stream_failure:
|
stream_failure:
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Router-id update from zebra daemon. */
|
/* Router-id update from zebra daemon. */
|
||||||
void zebra_router_id_update_read(struct stream *s, struct prefix *rid)
|
int zebra_router_id_update_read(struct stream *s, struct prefix *rid)
|
||||||
{
|
{
|
||||||
/* Fetch interface address. */
|
/* Fetch interface address. */
|
||||||
STREAM_GETC(s, rid->family);
|
STREAM_GETC(s, rid->family);
|
||||||
|
|
||||||
zclient_stream_get_prefix(s, rid);
|
return zclient_stream_get_prefix(s, rid);
|
||||||
|
|
||||||
stream_failure:
|
stream_failure:
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Interface addition from zebra daemon. */
|
/* Interface addition from zebra daemon. */
|
||||||
@ -1710,24 +1711,36 @@ stream_failure:
|
|||||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id)
|
static int zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id)
|
||||||
{
|
{
|
||||||
struct vrf *vrf;
|
struct vrf *vrf;
|
||||||
char vrfname_tmp[VRF_NAMSIZ];
|
char vrfname_tmp[VRF_NAMSIZ + 1] = {};
|
||||||
struct vrf_data data;
|
struct vrf_data data;
|
||||||
|
|
||||||
stream_get(&data, zclient->ibuf, sizeof(struct vrf_data));
|
STREAM_GET(&data, zclient->ibuf, sizeof(struct vrf_data));
|
||||||
/* Read interface name. */
|
/* Read interface name. */
|
||||||
stream_get(vrfname_tmp, zclient->ibuf, VRF_NAMSIZ);
|
STREAM_GET(vrfname_tmp, zclient->ibuf, VRF_NAMSIZ);
|
||||||
|
|
||||||
/* Lookup/create vrf by vrf_id. */
|
if (strlen(vrfname_tmp) == 0)
|
||||||
|
goto stream_failure;
|
||||||
|
|
||||||
|
/* Lookup/create vrf by name, then vrf_id. */
|
||||||
vrf = vrf_get(vrf_id, vrfname_tmp);
|
vrf = vrf_get(vrf_id, vrfname_tmp);
|
||||||
|
|
||||||
|
/* If there's already a VRF with this name, don't create vrf */
|
||||||
|
if (!vrf)
|
||||||
|
return 0;
|
||||||
|
|
||||||
vrf->data.l.table_id = data.l.table_id;
|
vrf->data.l.table_id = data.l.table_id;
|
||||||
memcpy(vrf->data.l.netns_name, data.l.netns_name, NS_NAMSIZ);
|
memcpy(vrf->data.l.netns_name, data.l.netns_name, NS_NAMSIZ);
|
||||||
/* overwrite default vrf */
|
/* overwrite default vrf */
|
||||||
if (vrf_id == VRF_DEFAULT)
|
if (vrf_id == VRF_DEFAULT)
|
||||||
vrf_set_default_name(vrfname_tmp, false);
|
vrf_set_default_name(vrfname_tmp, false);
|
||||||
vrf_enable(vrf);
|
vrf_enable(vrf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
stream_failure:
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zclient_vrf_delete(struct zclient *zclient, vrf_id_t vrf_id)
|
static void zclient_vrf_delete(struct zclient *zclient, vrf_id_t vrf_id)
|
||||||
@ -1748,21 +1761,32 @@ static void zclient_vrf_delete(struct zclient *zclient, vrf_id_t vrf_id)
|
|||||||
vrf_delete(vrf);
|
vrf_delete(vrf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zclient_interface_add(struct zclient *zclient, vrf_id_t vrf_id)
|
static int zclient_interface_add(struct zclient *zclient, vrf_id_t vrf_id)
|
||||||
{
|
{
|
||||||
struct interface *ifp;
|
struct interface *ifp;
|
||||||
char ifname_tmp[INTERFACE_NAMSIZ];
|
char ifname_tmp[INTERFACE_NAMSIZ + 1] = {};
|
||||||
struct stream *s = zclient->ibuf;
|
struct stream *s = zclient->ibuf;
|
||||||
|
|
||||||
/* Read interface name. */
|
/* Read interface name. */
|
||||||
stream_get(ifname_tmp, s, INTERFACE_NAMSIZ);
|
STREAM_GET(ifname_tmp, s, INTERFACE_NAMSIZ);
|
||||||
|
|
||||||
/* Lookup/create interface by name. */
|
/* Lookup/create interface by name. */
|
||||||
|
if (!vrf_get(vrf_id, NULL)) {
|
||||||
|
zlog_debug(
|
||||||
|
"Rx'd interface add from Zebra, but VRF %u does not exist",
|
||||||
|
vrf_id);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
ifp = if_get_by_name(ifname_tmp, vrf_id);
|
ifp = if_get_by_name(ifname_tmp, vrf_id);
|
||||||
|
|
||||||
zebra_interface_if_set_value(s, ifp);
|
zebra_interface_if_set_value(s, ifp);
|
||||||
|
|
||||||
if_new_via_zapi(ifp);
|
if_new_via_zapi(ifp);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
stream_failure:
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1774,10 +1798,10 @@ static void zclient_interface_add(struct zclient *zclient, vrf_id_t vrf_id)
|
|||||||
struct interface *zebra_interface_state_read(struct stream *s, vrf_id_t vrf_id)
|
struct interface *zebra_interface_state_read(struct stream *s, vrf_id_t vrf_id)
|
||||||
{
|
{
|
||||||
struct interface *ifp;
|
struct interface *ifp;
|
||||||
char ifname_tmp[INTERFACE_NAMSIZ];
|
char ifname_tmp[INTERFACE_NAMSIZ + 1] = {};
|
||||||
|
|
||||||
/* Read interface name. */
|
/* Read interface name. */
|
||||||
stream_get(ifname_tmp, s, INTERFACE_NAMSIZ);
|
STREAM_GET(ifname_tmp, s, INTERFACE_NAMSIZ);
|
||||||
|
|
||||||
/* Lookup this by interface index. */
|
/* Lookup this by interface index. */
|
||||||
ifp = if_lookup_by_name(ifname_tmp, vrf_id);
|
ifp = if_lookup_by_name(ifname_tmp, vrf_id);
|
||||||
@ -1791,6 +1815,8 @@ struct interface *zebra_interface_state_read(struct stream *s, vrf_id_t vrf_id)
|
|||||||
zebra_interface_if_set_value(s, ifp);
|
zebra_interface_if_set_value(s, ifp);
|
||||||
|
|
||||||
return ifp;
|
return ifp;
|
||||||
|
stream_failure:
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zclient_interface_delete(struct zclient *zclient, vrf_id_t vrf_id)
|
static void zclient_interface_delete(struct zclient *zclient, vrf_id_t vrf_id)
|
||||||
@ -1844,21 +1870,23 @@ static void zclient_handle_error(ZAPI_CALLBACK_ARGS)
|
|||||||
(*zclient->handle_error)(error);
|
(*zclient->handle_error)(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void link_params_set_value(struct stream *s, struct if_link_params *iflp)
|
static int link_params_set_value(struct stream *s, struct if_link_params *iflp)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (iflp == NULL)
|
if (iflp == NULL)
|
||||||
return;
|
return -1;
|
||||||
|
|
||||||
iflp->lp_status = stream_getl(s);
|
uint32_t bwclassnum;
|
||||||
iflp->te_metric = stream_getl(s);
|
|
||||||
iflp->max_bw = stream_getf(s);
|
STREAM_GETL(s, iflp->lp_status);
|
||||||
iflp->max_rsv_bw = stream_getf(s);
|
STREAM_GETL(s, iflp->te_metric);
|
||||||
uint32_t bwclassnum = stream_getl(s);
|
STREAM_GETF(s, iflp->max_bw);
|
||||||
|
STREAM_GETF(s, iflp->max_rsv_bw);
|
||||||
|
STREAM_GETL(s, bwclassnum);
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
for (i = 0; i < bwclassnum && i < MAX_CLASS_TYPE; i++)
|
for (i = 0; i < bwclassnum && i < MAX_CLASS_TYPE; i++)
|
||||||
iflp->unrsv_bw[i] = stream_getf(s);
|
STREAM_GETF(s, iflp->unrsv_bw[i]);
|
||||||
if (i < bwclassnum)
|
if (i < bwclassnum)
|
||||||
flog_err(
|
flog_err(
|
||||||
EC_LIB_ZAPI_MISSMATCH,
|
EC_LIB_ZAPI_MISSMATCH,
|
||||||
@ -1866,19 +1894,23 @@ static void link_params_set_value(struct stream *s, struct if_link_params *iflp)
|
|||||||
" - outdated library?",
|
" - outdated library?",
|
||||||
__func__, bwclassnum, MAX_CLASS_TYPE);
|
__func__, bwclassnum, MAX_CLASS_TYPE);
|
||||||
}
|
}
|
||||||
iflp->admin_grp = stream_getl(s);
|
STREAM_GETL(s, iflp->admin_grp);
|
||||||
iflp->rmt_as = stream_getl(s);
|
STREAM_GETL(s, iflp->rmt_as);
|
||||||
iflp->rmt_ip.s_addr = stream_get_ipv4(s);
|
iflp->rmt_ip.s_addr = stream_get_ipv4(s);
|
||||||
|
|
||||||
iflp->av_delay = stream_getl(s);
|
STREAM_GETL(s, iflp->av_delay);
|
||||||
iflp->min_delay = stream_getl(s);
|
STREAM_GETL(s, iflp->min_delay);
|
||||||
iflp->max_delay = stream_getl(s);
|
STREAM_GETL(s, iflp->max_delay);
|
||||||
iflp->delay_var = stream_getl(s);
|
STREAM_GETL(s, iflp->delay_var);
|
||||||
|
|
||||||
iflp->pkt_loss = stream_getf(s);
|
STREAM_GETF(s, iflp->pkt_loss);
|
||||||
iflp->res_bw = stream_getf(s);
|
STREAM_GETF(s, iflp->res_bw);
|
||||||
iflp->ava_bw = stream_getf(s);
|
STREAM_GETF(s, iflp->ava_bw);
|
||||||
iflp->use_bw = stream_getf(s);
|
STREAM_GETF(s, iflp->use_bw);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
stream_failure:
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct interface *zebra_interface_link_params_read(struct stream *s,
|
struct interface *zebra_interface_link_params_read(struct stream *s,
|
||||||
@ -1887,9 +1919,7 @@ struct interface *zebra_interface_link_params_read(struct stream *s,
|
|||||||
struct if_link_params *iflp;
|
struct if_link_params *iflp;
|
||||||
ifindex_t ifindex;
|
ifindex_t ifindex;
|
||||||
|
|
||||||
assert(s);
|
STREAM_GETL(s, ifindex);
|
||||||
|
|
||||||
ifindex = stream_getl(s);
|
|
||||||
|
|
||||||
struct interface *ifp = if_lookup_by_index(ifindex, vrf_id);
|
struct interface *ifp = if_lookup_by_index(ifindex, vrf_id);
|
||||||
|
|
||||||
@ -1903,36 +1933,41 @@ struct interface *zebra_interface_link_params_read(struct stream *s,
|
|||||||
if ((iflp = if_link_params_get(ifp)) == NULL)
|
if ((iflp = if_link_params_get(ifp)) == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
link_params_set_value(s, iflp);
|
if (link_params_set_value(s, iflp) != 0)
|
||||||
|
goto stream_failure;
|
||||||
|
|
||||||
return ifp;
|
return ifp;
|
||||||
|
|
||||||
|
stream_failure:
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zebra_interface_if_set_value(struct stream *s,
|
static void zebra_interface_if_set_value(struct stream *s,
|
||||||
struct interface *ifp)
|
struct interface *ifp)
|
||||||
{
|
{
|
||||||
uint8_t link_params_status = 0;
|
uint8_t link_params_status = 0;
|
||||||
ifindex_t old_ifindex;
|
ifindex_t old_ifindex, new_ifindex;
|
||||||
|
|
||||||
old_ifindex = ifp->ifindex;
|
old_ifindex = ifp->ifindex;
|
||||||
/* Read interface's index. */
|
/* Read interface's index. */
|
||||||
if_set_index(ifp, stream_getl(s));
|
STREAM_GETL(s, new_ifindex);
|
||||||
ifp->status = stream_getc(s);
|
if_set_index(ifp, new_ifindex);
|
||||||
|
STREAM_GETC(s, ifp->status);
|
||||||
|
|
||||||
/* Read interface's value. */
|
/* Read interface's value. */
|
||||||
ifp->flags = stream_getq(s);
|
STREAM_GETQ(s, ifp->flags);
|
||||||
ifp->ptm_enable = stream_getc(s);
|
STREAM_GETC(s, ifp->ptm_enable);
|
||||||
ifp->ptm_status = stream_getc(s);
|
STREAM_GETC(s, ifp->ptm_status);
|
||||||
ifp->metric = stream_getl(s);
|
STREAM_GETL(s, ifp->metric);
|
||||||
ifp->speed = stream_getl(s);
|
STREAM_GETL(s, ifp->speed);
|
||||||
ifp->mtu = stream_getl(s);
|
STREAM_GETL(s, ifp->mtu);
|
||||||
ifp->mtu6 = stream_getl(s);
|
STREAM_GETL(s, ifp->mtu6);
|
||||||
ifp->bandwidth = stream_getl(s);
|
STREAM_GETL(s, ifp->bandwidth);
|
||||||
ifp->link_ifindex = stream_getl(s);
|
STREAM_GETL(s, ifp->link_ifindex);
|
||||||
ifp->ll_type = stream_getl(s);
|
STREAM_GETL(s, ifp->ll_type);
|
||||||
ifp->hw_addr_len = stream_getl(s);
|
STREAM_GETL(s, ifp->hw_addr_len);
|
||||||
if (ifp->hw_addr_len)
|
if (ifp->hw_addr_len)
|
||||||
stream_get(ifp->hw_addr, s,
|
STREAM_GET(ifp->hw_addr, s,
|
||||||
MIN(ifp->hw_addr_len, INTERFACE_HWADDR_MAX));
|
MIN(ifp->hw_addr_len, INTERFACE_HWADDR_MAX));
|
||||||
|
|
||||||
/* Read Traffic Engineering status */
|
/* Read Traffic Engineering status */
|
||||||
@ -1944,6 +1979,11 @@ static void zebra_interface_if_set_value(struct stream *s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
nexthop_group_interface_state_change(ifp, old_ifindex);
|
nexthop_group_interface_state_change(ifp, old_ifindex);
|
||||||
|
|
||||||
|
return;
|
||||||
|
stream_failure:
|
||||||
|
zlog_err("Could not parse interface values; aborting");
|
||||||
|
assert(!"Failed to parse interface values");
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t zebra_interface_link_params_write(struct stream *s,
|
size_t zebra_interface_link_params_write(struct stream *s,
|
||||||
@ -2042,7 +2082,7 @@ struct connected *zebra_interface_address_read(int type, struct stream *s,
|
|||||||
memset(&d, 0, sizeof(d));
|
memset(&d, 0, sizeof(d));
|
||||||
|
|
||||||
/* Get interface index. */
|
/* Get interface index. */
|
||||||
ifindex = stream_getl(s);
|
STREAM_GETL(s, ifindex);
|
||||||
|
|
||||||
/* Lookup index. */
|
/* Lookup index. */
|
||||||
ifp = if_lookup_by_index(ifindex, vrf_id);
|
ifp = if_lookup_by_index(ifindex, vrf_id);
|
||||||
@ -2055,16 +2095,18 @@ struct connected *zebra_interface_address_read(int type, struct stream *s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Fetch flag. */
|
/* Fetch flag. */
|
||||||
ifc_flags = stream_getc(s);
|
STREAM_GETC(s, ifc_flags);
|
||||||
|
|
||||||
/* Fetch interface address. */
|
/* Fetch interface address. */
|
||||||
d.family = p.family = stream_getc(s);
|
STREAM_GETC(s, d.family);
|
||||||
|
p.family = d.family;
|
||||||
plen = prefix_blen(&d);
|
plen = prefix_blen(&d);
|
||||||
|
|
||||||
zclient_stream_get_prefix(s, &p);
|
if (zclient_stream_get_prefix(s, &p) != 0)
|
||||||
|
goto stream_failure;
|
||||||
|
|
||||||
/* Fetch destination address. */
|
/* Fetch destination address. */
|
||||||
stream_get(&d.u.prefix, s, plen);
|
STREAM_GET(&d.u.prefix, s, plen);
|
||||||
|
|
||||||
/* N.B. NULL destination pointers are encoded as all zeroes */
|
/* N.B. NULL destination pointers are encoded as all zeroes */
|
||||||
dp = memconstant(&d.u.prefix, 0, plen) ? NULL : &d;
|
dp = memconstant(&d.u.prefix, 0, plen) ? NULL : &d;
|
||||||
@ -2100,6 +2142,9 @@ struct connected *zebra_interface_address_read(int type, struct stream *s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ifc;
|
return ifc;
|
||||||
|
|
||||||
|
stream_failure:
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2135,7 +2180,7 @@ zebra_interface_nbr_address_read(int type, struct stream *s, vrf_id_t vrf_id)
|
|||||||
struct nbr_connected *ifc;
|
struct nbr_connected *ifc;
|
||||||
|
|
||||||
/* Get interface index. */
|
/* Get interface index. */
|
||||||
ifindex = stream_getl(s);
|
STREAM_GETL(s, ifindex);
|
||||||
|
|
||||||
/* Lookup index. */
|
/* Lookup index. */
|
||||||
ifp = if_lookup_by_index(ifindex, vrf_id);
|
ifp = if_lookup_by_index(ifindex, vrf_id);
|
||||||
@ -2148,9 +2193,9 @@ zebra_interface_nbr_address_read(int type, struct stream *s, vrf_id_t vrf_id)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.family = stream_getc(s);
|
STREAM_GETC(s, p.family);
|
||||||
stream_get(&p.u.prefix, s, prefix_blen(&p));
|
STREAM_GET(&p.u.prefix, s, prefix_blen(&p));
|
||||||
p.prefixlen = stream_getc(s);
|
STREAM_GETC(s, p.prefixlen);
|
||||||
|
|
||||||
if (type == ZEBRA_INTERFACE_NBR_ADDRESS_ADD) {
|
if (type == ZEBRA_INTERFACE_NBR_ADDRESS_ADD) {
|
||||||
/* Currently only supporting P2P links, so any new RA source
|
/* Currently only supporting P2P links, so any new RA source
|
||||||
@ -2174,18 +2219,21 @@ zebra_interface_nbr_address_read(int type, struct stream *s, vrf_id_t vrf_id)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ifc;
|
return ifc;
|
||||||
|
|
||||||
|
stream_failure:
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct interface *zebra_interface_vrf_update_read(struct stream *s,
|
struct interface *zebra_interface_vrf_update_read(struct stream *s,
|
||||||
vrf_id_t vrf_id,
|
vrf_id_t vrf_id,
|
||||||
vrf_id_t *new_vrf_id)
|
vrf_id_t *new_vrf_id)
|
||||||
{
|
{
|
||||||
char ifname[INTERFACE_NAMSIZ];
|
char ifname[INTERFACE_NAMSIZ + 1] = {};
|
||||||
struct interface *ifp;
|
struct interface *ifp;
|
||||||
vrf_id_t new_id;
|
vrf_id_t new_id;
|
||||||
|
|
||||||
/* Read interface name. */
|
/* Read interface name. */
|
||||||
stream_get(ifname, s, INTERFACE_NAMSIZ);
|
STREAM_GET(ifname, s, INTERFACE_NAMSIZ);
|
||||||
|
|
||||||
/* Lookup interface. */
|
/* Lookup interface. */
|
||||||
ifp = if_lookup_by_name(ifname, vrf_id);
|
ifp = if_lookup_by_name(ifname, vrf_id);
|
||||||
@ -2197,10 +2245,13 @@ struct interface *zebra_interface_vrf_update_read(struct stream *s,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Fetch new VRF Id. */
|
/* Fetch new VRF Id. */
|
||||||
new_id = stream_getl(s);
|
STREAM_GETL(s, new_id);
|
||||||
|
|
||||||
*new_vrf_id = new_id;
|
*new_vrf_id = new_id;
|
||||||
return ifp;
|
return ifp;
|
||||||
|
|
||||||
|
stream_failure:
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* filter unwanted messages until the expected one arrives */
|
/* filter unwanted messages until the expected one arrives */
|
||||||
@ -2309,8 +2360,11 @@ int lm_label_manager_connect(struct zclient *zclient, int async)
|
|||||||
s = zclient->ibuf;
|
s = zclient->ibuf;
|
||||||
|
|
||||||
/* read instance and proto */
|
/* read instance and proto */
|
||||||
uint8_t proto = stream_getc(s);
|
uint8_t proto;
|
||||||
uint16_t instance = stream_getw(s);
|
uint16_t instance;
|
||||||
|
|
||||||
|
STREAM_GETC(s, proto);
|
||||||
|
STREAM_GETW(s, instance);
|
||||||
|
|
||||||
/* sanity */
|
/* sanity */
|
||||||
if (proto != zclient->redist_default)
|
if (proto != zclient->redist_default)
|
||||||
@ -2325,11 +2379,14 @@ int lm_label_manager_connect(struct zclient *zclient, int async)
|
|||||||
instance, zclient->instance);
|
instance, zclient->instance);
|
||||||
|
|
||||||
/* result code */
|
/* result code */
|
||||||
result = stream_getc(s);
|
STREAM_GETC(s, result);
|
||||||
if (zclient_debug)
|
if (zclient_debug)
|
||||||
zlog_debug("LM connect-response received, result %u", result);
|
zlog_debug("LM connect-response received, result %u", result);
|
||||||
|
|
||||||
return (int)result;
|
return (int)result;
|
||||||
|
|
||||||
|
stream_failure:
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2437,8 +2494,11 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t base,
|
|||||||
s = zclient->ibuf;
|
s = zclient->ibuf;
|
||||||
|
|
||||||
/* read proto and instance */
|
/* read proto and instance */
|
||||||
uint8_t proto = stream_getc(s);
|
uint8_t proto;
|
||||||
uint16_t instance = stream_getw(s);
|
uint8_t instance;
|
||||||
|
|
||||||
|
STREAM_GETC(s, proto);
|
||||||
|
STREAM_GETW(s, instance);
|
||||||
|
|
||||||
/* sanities */
|
/* sanities */
|
||||||
if (proto != zclient->redist_default)
|
if (proto != zclient->redist_default)
|
||||||
@ -2460,10 +2520,10 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t base,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* keep */
|
/* keep */
|
||||||
response_keep = stream_getc(s);
|
STREAM_GETC(s, response_keep);
|
||||||
/* start and end labels */
|
/* start and end labels */
|
||||||
*start = stream_getl(s);
|
STREAM_GETL(s, *start);
|
||||||
*end = stream_getl(s);
|
STREAM_GETL(s, *end);
|
||||||
|
|
||||||
/* not owning this response */
|
/* not owning this response */
|
||||||
if (keep != response_keep) {
|
if (keep != response_keep) {
|
||||||
@ -2485,6 +2545,9 @@ int lm_get_label_chunk(struct zclient *zclient, uint8_t keep, uint32_t base,
|
|||||||
response_keep);
|
response_keep);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
stream_failure:
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2874,7 +2937,7 @@ int zebra_send_pw(struct zclient *zclient, int command, struct zapi_pw *pw)
|
|||||||
/*
|
/*
|
||||||
* Receive PW status update from Zebra and send it to LDE process.
|
* Receive PW status update from Zebra and send it to LDE process.
|
||||||
*/
|
*/
|
||||||
void zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw)
|
int zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw)
|
||||||
{
|
{
|
||||||
struct stream *s;
|
struct stream *s;
|
||||||
|
|
||||||
@ -2883,8 +2946,12 @@ void zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw)
|
|||||||
|
|
||||||
/* Get data. */
|
/* Get data. */
|
||||||
stream_get(pw->ifname, s, IF_NAMESIZE);
|
stream_get(pw->ifname, s, IF_NAMESIZE);
|
||||||
pw->ifindex = stream_getl(s);
|
STREAM_GETL(s, pw->ifindex);
|
||||||
pw->status = stream_getl(s);
|
STREAM_GETL(s, pw->status);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
stream_failure:
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void zclient_capability_decode(ZAPI_CALLBACK_ARGS)
|
static void zclient_capability_decode(ZAPI_CALLBACK_ARGS)
|
||||||
@ -2895,7 +2962,14 @@ static void zclient_capability_decode(ZAPI_CALLBACK_ARGS)
|
|||||||
uint8_t mpls_enabled;
|
uint8_t mpls_enabled;
|
||||||
|
|
||||||
STREAM_GETL(s, vrf_backend);
|
STREAM_GETL(s, vrf_backend);
|
||||||
vrf_configure_backend(vrf_backend);
|
|
||||||
|
if (vrf_backend < 0 || vrf_configure_backend(vrf_backend)) {
|
||||||
|
flog_err(EC_LIB_ZAPI_ENCODE,
|
||||||
|
"%s: Garbage VRF backend type: %d\n", __func__,
|
||||||
|
vrf_backend);
|
||||||
|
goto stream_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
memset(&cap, 0, sizeof(cap));
|
memset(&cap, 0, sizeof(cap));
|
||||||
STREAM_GETC(s, mpls_enabled);
|
STREAM_GETC(s, mpls_enabled);
|
||||||
|
@ -723,7 +723,7 @@ zebra_interface_nbr_address_read(int, struct stream *, vrf_id_t);
|
|||||||
extern struct interface *zebra_interface_vrf_update_read(struct stream *s,
|
extern struct interface *zebra_interface_vrf_update_read(struct stream *s,
|
||||||
vrf_id_t vrf_id,
|
vrf_id_t vrf_id,
|
||||||
vrf_id_t *new_vrf_id);
|
vrf_id_t *new_vrf_id);
|
||||||
extern void zebra_router_id_update_read(struct stream *s, struct prefix *rid);
|
extern int zebra_router_id_update_read(struct stream *s, struct prefix *rid);
|
||||||
|
|
||||||
extern struct interface *zebra_interface_link_params_read(struct stream *s,
|
extern struct interface *zebra_interface_link_params_read(struct stream *s,
|
||||||
vrf_id_t vrf_id);
|
vrf_id_t vrf_id);
|
||||||
@ -752,7 +752,8 @@ extern int zapi_labels_decode(struct stream *s, struct zapi_labels *zl);
|
|||||||
|
|
||||||
extern int zebra_send_pw(struct zclient *zclient, int command,
|
extern int zebra_send_pw(struct zclient *zclient, int command,
|
||||||
struct zapi_pw *pw);
|
struct zapi_pw *pw);
|
||||||
extern void zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS, struct zapi_pw_status *pw);
|
extern int zebra_read_pw_status_update(ZAPI_CALLBACK_ARGS,
|
||||||
|
struct zapi_pw_status *pw);
|
||||||
|
|
||||||
extern int zclient_route_send(uint8_t, struct zclient *, struct zapi_route *);
|
extern int zclient_route_send(uint8_t, struct zclient *, struct zapi_route *);
|
||||||
extern int zclient_send_rnh(struct zclient *zclient, int command,
|
extern int zclient_send_rnh(struct zclient *zclient, int command,
|
||||||
|
Loading…
Reference in New Issue
Block a user