Commit 2dc47640 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'tools-ynl-user-space-c'

Jakub Kicinski says:

====================
tools: ynl: user space C

Use the code gen which is already in tree to generate a user space
library for a handful of simple families. I find YNL C quite useful
in some WIP projects, and I think others may find it useful, too.
I was hoping someone will pick this work up and finish it...
but it seems that Python YNL has largely stolen the thunder.
Python may not be great for selftest, tho, and actually this lib
is more fully-featured. The Python script was meant as a quick demo,
funny how those things go.

v2: https://lore.kernel.org/all/20230604175843.662084-1-kuba@kernel.org/
v1: https://lore.kernel.org/all/20230603052547.631384-1-kuba@kernel.org/
====================

Link: https://lore.kernel.org/r/20230605190108.809439-1-kuba@kernel.org


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents ae91f7e4 ee0202e2
Loading
Loading
Loading
Loading
+79 −0
Original line number Diff line number Diff line
@@ -78,3 +78,82 @@ to see other examples.
The code generation itself is performed by ``tools/net/ynl/ynl-gen-c.py``
but it takes a few arguments so calling it directly for each file
quickly becomes tedious.

YNL lib
=======

``tools/net/ynl/lib/`` contains an implementation of a C library
(based on libmnl) which integrates with code generated by
``tools/net/ynl/ynl-gen-c.py`` to create easy to use netlink wrappers.

YNL basics
----------

The YNL library consists of two parts - the generic code (functions
prefix by ``ynl_``) and per-family auto-generated code (prefixed
with the name of the family).

To create a YNL socket call ynl_sock_create() passing the family
struct (family structs are exported by the auto-generated code).
ynl_sock_destroy() closes the socket.

YNL requests
------------

Steps for issuing YNL requests are best explained on an example.
All the functions and types in this example come from the auto-generated
code (for the netdev family in this case):

.. code-block:: c

   // 0. Request and response pointers
   struct netdev_dev_get_req *req;
   struct netdev_dev_get_rsp *d;

   // 1. Allocate a request
   req = netdev_dev_get_req_alloc();
   // 2. Set request parameters (as needed)
   netdev_dev_get_req_set_ifindex(req, ifindex);

   // 3. Issues the request
   d = netdev_dev_get(ys, req);
   // 4. Free the request arguments
   netdev_dev_get_req_free(req);
   // 5. Error check (the return value from step 3)
   if (!d) {
	// 6. Print the YNL-generated error
	fprintf(stderr, "YNL: %s\n", ys->err.msg);
        return -1;
   }

   // ... do stuff with the response @d

   // 7. Free response
   netdev_dev_get_rsp_free(d);

YNL dumps
---------

Performing dumps follows similar pattern as requests.
Dumps return a list of objects terminated by a special marker,
or NULL on error. Use ``ynl_dump_foreach()`` to iterate over
the result.

YNL notifications
-----------------

YNL lib supports using the same socket for notifications and
requests. In case notifications arrive during processing of a request
they are queued internally and can be retrieved at a later time.

To subscribed to notifications use ``ynl_subscribe()``.
The notifications have to be read out from the socket,
``ynl_socket_get_fd()`` returns the underlying socket fd which can
be plugged into appropriate asynchronous IO API like ``poll``,
or ``select``.

Notifications can be retrieved using ``ynl_ntf_dequeue()`` and have
to be freed using ``ynl_ntf_free()``. Since we don't know the notification
type upfront the notifications are returned as ``struct ynl_ntf_base_type *``
and user is expected to cast them to the appropriate full type based
on the ``cmd`` member.

tools/net/ynl/Makefile

0 → 100644
+19 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0

SUBDIRS = lib generated samples

all: $(SUBDIRS)

$(SUBDIRS):
	@if [ -f "$@/Makefile" ] ; then \
		$(MAKE) -C $@ ; \
	fi

clean hardclean:
	@for dir in $(SUBDIRS) ; do \
		if [ -f "$$dir/Makefile" ] ; then \
			$(MAKE) -C $$dir $@; \
		fi \
	done

.PHONY: clean all $(SUBDIRS)
+45 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0

CC=gcc
CFLAGS=-std=gnu11 -O2 -W -Wall -Wextra -Wno-unused-parameter -Wshadow \
	-I../lib/
ifeq ("$(DEBUG)","1")
  CFLAGS += -g -fsanitize=address -fsanitize=leak -static-libasan
endif

TOOL:=../ynl-gen-c.py

GENS:=fou netdev
SRCS=$(patsubst %,%-user.c,${GENS})
HDRS=$(patsubst %,%-user.h,${GENS})
OBJS=$(patsubst %,%-user.o,${GENS})

all: protos.a $(HDRS) $(SRCS) $(KHDRS) $(KSRCS) $(UAPI) regen

protos.a: $(OBJS)
	@echo -e "\tAR $@"
	@ar rcs $@ $(OBJS)

%-user.h: ../../../../Documentation/netlink/specs/%.yaml $(TOOL)
	@echo -e "\tGEN $@"
	@$(TOOL) --mode user --header --spec $< > $@

%-user.c: ../../../../Documentation/netlink/specs/%.yaml $(TOOL)
	@echo -e "\tGEN $@"
	@$(TOOL) --mode user --source --spec $< > $@

%-user.o: %-user.c %-user.h
	@echo -e "\tCC $@"
	@$(COMPILE.c) -c -o $@ $<

clean:
	rm -f *.o

hardclean: clean
	rm -f *.c *.h *.a

regen:
	@../ynl-regen.sh

.PHONY: all clean hardclean regen
.DEFAULT_GOAL: all
+340 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
/* Do not edit directly, auto-generated from: */
/*	Documentation/netlink/specs/fou.yaml */
/* YNL-GEN user source */

#include <stdlib.h>
#include "fou-user.h"
#include "ynl.h"
#include <linux/fou.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>

/* Enums */
static const char * const fou_op_strmap[] = {
	[FOU_CMD_UNSPEC] = "unspec",
	[FOU_CMD_ADD] = "add",
	[FOU_CMD_DEL] = "del",
	[FOU_CMD_GET] = "get",
};

const char *fou_op_str(int op)
{
	if (op < 0 || op >= (int)MNL_ARRAY_SIZE(fou_op_strmap))
		return NULL;
	return fou_op_strmap[op];
}

static const char * const fou_encap_type_strmap[] = {
	[0] = "unspec",
	[1] = "direct",
	[2] = "gue",
};

const char *fou_encap_type_str(int value)
{
	if (value < 0 || value >= (int)MNL_ARRAY_SIZE(fou_encap_type_strmap))
		return NULL;
	return fou_encap_type_strmap[value];
}

/* Policies */
extern struct ynl_policy_nest fou_nest;

struct ynl_policy_attr fou_policy[FOU_ATTR_MAX + 1] = {
	[FOU_ATTR_UNSPEC] = { .name = "unspec", .type = YNL_PT_REJECT, },
	[FOU_ATTR_PORT] = { .name = "port", .type = YNL_PT_U16, },
	[FOU_ATTR_AF] = { .name = "af", .type = YNL_PT_U8, },
	[FOU_ATTR_IPPROTO] = { .name = "ipproto", .type = YNL_PT_U8, },
	[FOU_ATTR_TYPE] = { .name = "type", .type = YNL_PT_U8, },
	[FOU_ATTR_REMCSUM_NOPARTIAL] = { .name = "remcsum_nopartial", .type = YNL_PT_FLAG, },
	[FOU_ATTR_LOCAL_V4] = { .name = "local_v4", .type = YNL_PT_U32, },
	[FOU_ATTR_LOCAL_V6] = { .name = "local_v6", .type = YNL_PT_BINARY,},
	[FOU_ATTR_PEER_V4] = { .name = "peer_v4", .type = YNL_PT_U32, },
	[FOU_ATTR_PEER_V6] = { .name = "peer_v6", .type = YNL_PT_BINARY,},
	[FOU_ATTR_PEER_PORT] = { .name = "peer_port", .type = YNL_PT_U16, },
	[FOU_ATTR_IFINDEX] = { .name = "ifindex", .type = YNL_PT_U32, },
};

struct ynl_policy_nest fou_nest = {
	.max_attr = FOU_ATTR_MAX,
	.table = fou_policy,
};

/* Common nested types */
/* ============== FOU_CMD_ADD ============== */
/* FOU_CMD_ADD - do */
void fou_add_req_free(struct fou_add_req *req)
{
	free(req->local_v6);
	free(req->peer_v6);
	free(req);
}

int fou_add(struct ynl_sock *ys, struct fou_add_req *req)
{
	struct nlmsghdr *nlh;
	int err;

	nlh = ynl_gemsg_start_req(ys, ys->family_id, FOU_CMD_ADD, 1);
	ys->req_policy = &fou_nest;

	if (req->_present.port)
		mnl_attr_put_u16(nlh, FOU_ATTR_PORT, req->port);
	if (req->_present.ipproto)
		mnl_attr_put_u8(nlh, FOU_ATTR_IPPROTO, req->ipproto);
	if (req->_present.type)
		mnl_attr_put_u8(nlh, FOU_ATTR_TYPE, req->type);
	if (req->_present.remcsum_nopartial)
		mnl_attr_put(nlh, FOU_ATTR_REMCSUM_NOPARTIAL, 0, NULL);
	if (req->_present.local_v4)
		mnl_attr_put_u32(nlh, FOU_ATTR_LOCAL_V4, req->local_v4);
	if (req->_present.peer_v4)
		mnl_attr_put_u32(nlh, FOU_ATTR_PEER_V4, req->peer_v4);
	if (req->_present.local_v6_len)
		mnl_attr_put(nlh, FOU_ATTR_LOCAL_V6, req->_present.local_v6_len, req->local_v6);
	if (req->_present.peer_v6_len)
		mnl_attr_put(nlh, FOU_ATTR_PEER_V6, req->_present.peer_v6_len, req->peer_v6);
	if (req->_present.peer_port)
		mnl_attr_put_u16(nlh, FOU_ATTR_PEER_PORT, req->peer_port);
	if (req->_present.ifindex)
		mnl_attr_put_u32(nlh, FOU_ATTR_IFINDEX, req->ifindex);

	err = ynl_exec(ys, nlh, NULL);
	if (err < 0)
		return -1;

	return 0;
}

/* ============== FOU_CMD_DEL ============== */
/* FOU_CMD_DEL - do */
void fou_del_req_free(struct fou_del_req *req)
{
	free(req->local_v6);
	free(req->peer_v6);
	free(req);
}

int fou_del(struct ynl_sock *ys, struct fou_del_req *req)
{
	struct nlmsghdr *nlh;
	int err;

	nlh = ynl_gemsg_start_req(ys, ys->family_id, FOU_CMD_DEL, 1);
	ys->req_policy = &fou_nest;

	if (req->_present.af)
		mnl_attr_put_u8(nlh, FOU_ATTR_AF, req->af);
	if (req->_present.ifindex)
		mnl_attr_put_u32(nlh, FOU_ATTR_IFINDEX, req->ifindex);
	if (req->_present.port)
		mnl_attr_put_u16(nlh, FOU_ATTR_PORT, req->port);
	if (req->_present.peer_port)
		mnl_attr_put_u16(nlh, FOU_ATTR_PEER_PORT, req->peer_port);
	if (req->_present.local_v4)
		mnl_attr_put_u32(nlh, FOU_ATTR_LOCAL_V4, req->local_v4);
	if (req->_present.peer_v4)
		mnl_attr_put_u32(nlh, FOU_ATTR_PEER_V4, req->peer_v4);
	if (req->_present.local_v6_len)
		mnl_attr_put(nlh, FOU_ATTR_LOCAL_V6, req->_present.local_v6_len, req->local_v6);
	if (req->_present.peer_v6_len)
		mnl_attr_put(nlh, FOU_ATTR_PEER_V6, req->_present.peer_v6_len, req->peer_v6);

	err = ynl_exec(ys, nlh, NULL);
	if (err < 0)
		return -1;

	return 0;
}

/* ============== FOU_CMD_GET ============== */
/* FOU_CMD_GET - do */
void fou_get_req_free(struct fou_get_req *req)
{
	free(req->local_v6);
	free(req->peer_v6);
	free(req);
}

void fou_get_rsp_free(struct fou_get_rsp *rsp)
{
	free(rsp->local_v6);
	free(rsp->peer_v6);
	free(rsp);
}

int fou_get_rsp_parse(const struct nlmsghdr *nlh, void *data)
{
	struct ynl_parse_arg *yarg = data;
	const struct nlattr *attr;
	struct fou_get_rsp *dst;

	dst = yarg->data;

	mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) {
		if (mnl_attr_get_type(attr) == FOU_ATTR_PORT) {
			if (ynl_attr_validate(yarg, attr))
				return MNL_CB_ERROR;
			dst->_present.port = 1;
			dst->port = mnl_attr_get_u16(attr);
		}
		else if (mnl_attr_get_type(attr) == FOU_ATTR_IPPROTO) {
			if (ynl_attr_validate(yarg, attr))
				return MNL_CB_ERROR;
			dst->_present.ipproto = 1;
			dst->ipproto = mnl_attr_get_u8(attr);
		}
		else if (mnl_attr_get_type(attr) == FOU_ATTR_TYPE) {
			if (ynl_attr_validate(yarg, attr))
				return MNL_CB_ERROR;
			dst->_present.type = 1;
			dst->type = mnl_attr_get_u8(attr);
		}
		else if (mnl_attr_get_type(attr) == FOU_ATTR_REMCSUM_NOPARTIAL) {
			if (ynl_attr_validate(yarg, attr))
				return MNL_CB_ERROR;
			dst->_present.remcsum_nopartial = 1;
		}
		else if (mnl_attr_get_type(attr) == FOU_ATTR_LOCAL_V4) {
			if (ynl_attr_validate(yarg, attr))
				return MNL_CB_ERROR;
			dst->_present.local_v4 = 1;
			dst->local_v4 = mnl_attr_get_u32(attr);
		}
		else if (mnl_attr_get_type(attr) == FOU_ATTR_PEER_V4) {
			if (ynl_attr_validate(yarg, attr))
				return MNL_CB_ERROR;
			dst->_present.peer_v4 = 1;
			dst->peer_v4 = mnl_attr_get_u32(attr);
		}
		else if (mnl_attr_get_type(attr) == FOU_ATTR_LOCAL_V6) {
			unsigned int len;

			if (ynl_attr_validate(yarg, attr))
				return MNL_CB_ERROR;

			len = mnl_attr_get_payload_len(attr);
			dst->_present.local_v6_len = len;
			dst->local_v6 = malloc(len);
			memcpy(dst->local_v6, mnl_attr_get_payload(attr), len);
		}
		else if (mnl_attr_get_type(attr) == FOU_ATTR_PEER_V6) {
			unsigned int len;

			if (ynl_attr_validate(yarg, attr))
				return MNL_CB_ERROR;

			len = mnl_attr_get_payload_len(attr);
			dst->_present.peer_v6_len = len;
			dst->peer_v6 = malloc(len);
			memcpy(dst->peer_v6, mnl_attr_get_payload(attr), len);
		}
		else if (mnl_attr_get_type(attr) == FOU_ATTR_PEER_PORT) {
			if (ynl_attr_validate(yarg, attr))
				return MNL_CB_ERROR;
			dst->_present.peer_port = 1;
			dst->peer_port = mnl_attr_get_u16(attr);
		}
		else if (mnl_attr_get_type(attr) == FOU_ATTR_IFINDEX) {
			if (ynl_attr_validate(yarg, attr))
				return MNL_CB_ERROR;
			dst->_present.ifindex = 1;
			dst->ifindex = mnl_attr_get_u32(attr);
		}
	}

	return MNL_CB_OK;
}

struct fou_get_rsp *fou_get(struct ynl_sock *ys, struct fou_get_req *req)
{
	struct ynl_req_state yrs = { .yarg = { .ys = ys, }, };
	struct fou_get_rsp *rsp;
	struct nlmsghdr *nlh;
	int err;

	nlh = ynl_gemsg_start_req(ys, ys->family_id, FOU_CMD_GET, 1);
	ys->req_policy = &fou_nest;
	yrs.yarg.rsp_policy = &fou_nest;

	if (req->_present.af)
		mnl_attr_put_u8(nlh, FOU_ATTR_AF, req->af);
	if (req->_present.ifindex)
		mnl_attr_put_u32(nlh, FOU_ATTR_IFINDEX, req->ifindex);
	if (req->_present.port)
		mnl_attr_put_u16(nlh, FOU_ATTR_PORT, req->port);
	if (req->_present.peer_port)
		mnl_attr_put_u16(nlh, FOU_ATTR_PEER_PORT, req->peer_port);
	if (req->_present.local_v4)
		mnl_attr_put_u32(nlh, FOU_ATTR_LOCAL_V4, req->local_v4);
	if (req->_present.peer_v4)
		mnl_attr_put_u32(nlh, FOU_ATTR_PEER_V4, req->peer_v4);
	if (req->_present.local_v6_len)
		mnl_attr_put(nlh, FOU_ATTR_LOCAL_V6, req->_present.local_v6_len, req->local_v6);
	if (req->_present.peer_v6_len)
		mnl_attr_put(nlh, FOU_ATTR_PEER_V6, req->_present.peer_v6_len, req->peer_v6);

	rsp = calloc(1, sizeof(*rsp));
	yrs.yarg.data = rsp;
	yrs.cb = fou_get_rsp_parse;
	yrs.rsp_cmd = FOU_CMD_GET;

	err = ynl_exec(ys, nlh, &yrs);
	if (err < 0)
		goto err_free;

	return rsp;

err_free:
	fou_get_rsp_free(rsp);
	return NULL;
}

/* FOU_CMD_GET - dump */
void fou_get_list_free(struct fou_get_list *rsp)
{
	struct fou_get_list *next = rsp;

	while ((void *)next != YNL_LIST_END) {
		rsp = next;
		next = rsp->next;

		free(rsp->obj.local_v6);
		free(rsp->obj.peer_v6);
		free(rsp);
	}
}

struct fou_get_list *fou_get_dump(struct ynl_sock *ys)
{
	struct ynl_dump_state yds = {};
	struct nlmsghdr *nlh;
	int err;

	yds.ys = ys;
	yds.alloc_sz = sizeof(struct fou_get_list);
	yds.cb = fou_get_rsp_parse;
	yds.rsp_cmd = FOU_CMD_GET;
	yds.rsp_policy = &fou_nest;

	nlh = ynl_gemsg_start_dump(ys, ys->family_id, FOU_CMD_GET, 1);

	err = ynl_exec_dump(ys, nlh, &yds);
	if (err < 0)
		goto free_list;

	return yds.first;

free_list:
	fou_get_list_free(yds.first);
	return NULL;
}

const struct ynl_family ynl_fou_family =  {
	.name		= "fou",
};
+337 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
/* Do not edit directly, auto-generated from: */
/*	Documentation/netlink/specs/fou.yaml */
/* YNL-GEN user header */

#ifndef _LINUX_FOU_GEN_H
#define _LINUX_FOU_GEN_H

#include <stdlib.h>
#include <string.h>
#include <linux/types.h>
#include <linux/fou.h>

struct ynl_sock;

extern const struct ynl_family ynl_fou_family;

/* Enums */
const char *fou_op_str(int op);
const char *fou_encap_type_str(int value);

/* Common nested types */
/* ============== FOU_CMD_ADD ============== */
/* FOU_CMD_ADD - do */
struct fou_add_req {
	struct {
		__u32 port:1;
		__u32 ipproto:1;
		__u32 type:1;
		__u32 remcsum_nopartial:1;
		__u32 local_v4:1;
		__u32 peer_v4:1;
		__u32 local_v6_len;
		__u32 peer_v6_len;
		__u32 peer_port:1;
		__u32 ifindex:1;
	} _present;

	__u16 port /* big-endian */;
	__u8 ipproto;
	__u8 type;
	__u32 local_v4;
	__u32 peer_v4;
	void *local_v6;
	void *peer_v6;
	__u16 peer_port /* big-endian */;
	__s32 ifindex;
};

static inline struct fou_add_req *fou_add_req_alloc(void)
{
	return calloc(1, sizeof(struct fou_add_req));
}
void fou_add_req_free(struct fou_add_req *req);

static inline void
fou_add_req_set_port(struct fou_add_req *req, __u16 port /* big-endian */)
{
	req->_present.port = 1;
	req->port = port;
}
static inline void
fou_add_req_set_ipproto(struct fou_add_req *req, __u8 ipproto)
{
	req->_present.ipproto = 1;
	req->ipproto = ipproto;
}
static inline void fou_add_req_set_type(struct fou_add_req *req, __u8 type)
{
	req->_present.type = 1;
	req->type = type;
}
static inline void fou_add_req_set_remcsum_nopartial(struct fou_add_req *req)
{
	req->_present.remcsum_nopartial = 1;
}
static inline void
fou_add_req_set_local_v4(struct fou_add_req *req, __u32 local_v4)
{
	req->_present.local_v4 = 1;
	req->local_v4 = local_v4;
}
static inline void
fou_add_req_set_peer_v4(struct fou_add_req *req, __u32 peer_v4)
{
	req->_present.peer_v4 = 1;
	req->peer_v4 = peer_v4;
}
static inline void
fou_add_req_set_local_v6(struct fou_add_req *req, const void *local_v6,
			 size_t len)
{
	free(req->local_v6);
	req->local_v6 = malloc(req->_present.local_v6_len);
	memcpy(req->local_v6, local_v6, req->_present.local_v6_len);
}
static inline void
fou_add_req_set_peer_v6(struct fou_add_req *req, const void *peer_v6,
			size_t len)
{
	free(req->peer_v6);
	req->peer_v6 = malloc(req->_present.peer_v6_len);
	memcpy(req->peer_v6, peer_v6, req->_present.peer_v6_len);
}
static inline void
fou_add_req_set_peer_port(struct fou_add_req *req,
			  __u16 peer_port /* big-endian */)
{
	req->_present.peer_port = 1;
	req->peer_port = peer_port;
}
static inline void
fou_add_req_set_ifindex(struct fou_add_req *req, __s32 ifindex)
{
	req->_present.ifindex = 1;
	req->ifindex = ifindex;
}

/*
 * Add port.
 */
int fou_add(struct ynl_sock *ys, struct fou_add_req *req);

/* ============== FOU_CMD_DEL ============== */
/* FOU_CMD_DEL - do */
struct fou_del_req {
	struct {
		__u32 af:1;
		__u32 ifindex:1;
		__u32 port:1;
		__u32 peer_port:1;
		__u32 local_v4:1;
		__u32 peer_v4:1;
		__u32 local_v6_len;
		__u32 peer_v6_len;
	} _present;

	__u8 af;
	__s32 ifindex;
	__u16 port /* big-endian */;
	__u16 peer_port /* big-endian */;
	__u32 local_v4;
	__u32 peer_v4;
	void *local_v6;
	void *peer_v6;
};

static inline struct fou_del_req *fou_del_req_alloc(void)
{
	return calloc(1, sizeof(struct fou_del_req));
}
void fou_del_req_free(struct fou_del_req *req);

static inline void fou_del_req_set_af(struct fou_del_req *req, __u8 af)
{
	req->_present.af = 1;
	req->af = af;
}
static inline void
fou_del_req_set_ifindex(struct fou_del_req *req, __s32 ifindex)
{
	req->_present.ifindex = 1;
	req->ifindex = ifindex;
}
static inline void
fou_del_req_set_port(struct fou_del_req *req, __u16 port /* big-endian */)
{
	req->_present.port = 1;
	req->port = port;
}
static inline void
fou_del_req_set_peer_port(struct fou_del_req *req,
			  __u16 peer_port /* big-endian */)
{
	req->_present.peer_port = 1;
	req->peer_port = peer_port;
}
static inline void
fou_del_req_set_local_v4(struct fou_del_req *req, __u32 local_v4)
{
	req->_present.local_v4 = 1;
	req->local_v4 = local_v4;
}
static inline void
fou_del_req_set_peer_v4(struct fou_del_req *req, __u32 peer_v4)
{
	req->_present.peer_v4 = 1;
	req->peer_v4 = peer_v4;
}
static inline void
fou_del_req_set_local_v6(struct fou_del_req *req, const void *local_v6,
			 size_t len)
{
	free(req->local_v6);
	req->local_v6 = malloc(req->_present.local_v6_len);
	memcpy(req->local_v6, local_v6, req->_present.local_v6_len);
}
static inline void
fou_del_req_set_peer_v6(struct fou_del_req *req, const void *peer_v6,
			size_t len)
{
	free(req->peer_v6);
	req->peer_v6 = malloc(req->_present.peer_v6_len);
	memcpy(req->peer_v6, peer_v6, req->_present.peer_v6_len);
}

/*
 * Delete port.
 */
int fou_del(struct ynl_sock *ys, struct fou_del_req *req);

/* ============== FOU_CMD_GET ============== */
/* FOU_CMD_GET - do */
struct fou_get_req {
	struct {
		__u32 af:1;
		__u32 ifindex:1;
		__u32 port:1;
		__u32 peer_port:1;
		__u32 local_v4:1;
		__u32 peer_v4:1;
		__u32 local_v6_len;
		__u32 peer_v6_len;
	} _present;

	__u8 af;
	__s32 ifindex;
	__u16 port /* big-endian */;
	__u16 peer_port /* big-endian */;
	__u32 local_v4;
	__u32 peer_v4;
	void *local_v6;
	void *peer_v6;
};

static inline struct fou_get_req *fou_get_req_alloc(void)
{
	return calloc(1, sizeof(struct fou_get_req));
}
void fou_get_req_free(struct fou_get_req *req);

static inline void fou_get_req_set_af(struct fou_get_req *req, __u8 af)
{
	req->_present.af = 1;
	req->af = af;
}
static inline void
fou_get_req_set_ifindex(struct fou_get_req *req, __s32 ifindex)
{
	req->_present.ifindex = 1;
	req->ifindex = ifindex;
}
static inline void
fou_get_req_set_port(struct fou_get_req *req, __u16 port /* big-endian */)
{
	req->_present.port = 1;
	req->port = port;
}
static inline void
fou_get_req_set_peer_port(struct fou_get_req *req,
			  __u16 peer_port /* big-endian */)
{
	req->_present.peer_port = 1;
	req->peer_port = peer_port;
}
static inline void
fou_get_req_set_local_v4(struct fou_get_req *req, __u32 local_v4)
{
	req->_present.local_v4 = 1;
	req->local_v4 = local_v4;
}
static inline void
fou_get_req_set_peer_v4(struct fou_get_req *req, __u32 peer_v4)
{
	req->_present.peer_v4 = 1;
	req->peer_v4 = peer_v4;
}
static inline void
fou_get_req_set_local_v6(struct fou_get_req *req, const void *local_v6,
			 size_t len)
{
	free(req->local_v6);
	req->local_v6 = malloc(req->_present.local_v6_len);
	memcpy(req->local_v6, local_v6, req->_present.local_v6_len);
}
static inline void
fou_get_req_set_peer_v6(struct fou_get_req *req, const void *peer_v6,
			size_t len)
{
	free(req->peer_v6);
	req->peer_v6 = malloc(req->_present.peer_v6_len);
	memcpy(req->peer_v6, peer_v6, req->_present.peer_v6_len);
}

struct fou_get_rsp {
	struct {
		__u32 port:1;
		__u32 ipproto:1;
		__u32 type:1;
		__u32 remcsum_nopartial:1;
		__u32 local_v4:1;
		__u32 peer_v4:1;
		__u32 local_v6_len;
		__u32 peer_v6_len;
		__u32 peer_port:1;
		__u32 ifindex:1;
	} _present;

	__u16 port /* big-endian */;
	__u8 ipproto;
	__u8 type;
	__u32 local_v4;
	__u32 peer_v4;
	void *local_v6;
	void *peer_v6;
	__u16 peer_port /* big-endian */;
	__s32 ifindex;
};

void fou_get_rsp_free(struct fou_get_rsp *rsp);

/*
 * Get tunnel info.
 */
struct fou_get_rsp *fou_get(struct ynl_sock *ys, struct fou_get_req *req);

/* FOU_CMD_GET - dump */
struct fou_get_list {
	struct fou_get_list *next;
	struct fou_get_rsp obj __attribute__ ((aligned (8)));
};

void fou_get_list_free(struct fou_get_list *rsp);

struct fou_get_list *fou_get_dump(struct ynl_sock *ys);

#endif /* _LINUX_FOU_GEN_H */
Loading