Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/tools/net/ynl/lib/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 23 kB image not shown  

Quelle  ynl.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
#include <errno.h>
#include <poll.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <linux/types.h>
#include <linux/genetlink.h>
#include <sys/socket.h>

#include "ynl.h"

#define ARRAY_SIZE(arr)  (sizeof(arr) / sizeof(*arr))

#define __yerr_msg(yse, _msg...)     \
 ({        \
  struct ynl_error *_yse = (yse);    \
         \
  if (_yse) {      \
   snprintf(_yse->msg, sizeof(_yse->msg) - 1,  _msg); \
   _yse->msg[sizeof(_yse->msg) - 1] = 0;  \
  }       \
 })

#define __yerr_code(yse, _code...)  \
 ({     \
  struct ynl_error *_yse = (yse); \
      \
  if (_yse) {   \
   _yse->code = _code; \
  }    \
 })

#define __yerr(yse, _code, _msg...)  \
 ({     \
  __yerr_msg(yse, _msg);  \
  __yerr_code(yse, _code); \
 })

#define __perr(yse, _msg)  __yerr(yse, errno, _msg)

#define yerr_msg(_ys, _msg...)  __yerr_msg(&(_ys)->err, _msg)
#define yerr(_ys, _code, _msg...) __yerr(&(_ys)->err, _code, _msg)
#define perr(_ys, _msg)   __yerr(&(_ys)->err, errno, _msg)

/* -- Netlink boiler plate */
static bool
ynl_err_walk_is_sel(const struct ynl_policy_nest *policy,
      const struct nlattr *attr)
{
 unsigned int type = ynl_attr_type(attr);

 return policy && type <= policy->max_attr &&
  policy->table[type].is_selector;
}

static const struct ynl_policy_nest *
ynl_err_walk_sel_policy(const struct ynl_policy_attr *policy_attr,
   const struct nlattr *selector)
{
 const struct ynl_policy_nest *policy = policy_attr->nest;
 const char *sel;
 unsigned int i;

 if (!policy_attr->is_submsg)
  return policy;

 sel = ynl_attr_get_str(selector);
 for (i = 0; i <= policy->max_attr; i++) {
  if (!strcmp(sel, policy->table[i].name))
   return policy->table[i].nest;
 }

 return NULL;
}

static int
ynl_err_walk_report_one(const struct ynl_policy_nest *policy,
   const struct nlattr *selector, unsigned int type,
   char *str, int str_sz, int *n)
{
 if (!policy) {
  if (*n < str_sz)
   *n += snprintf(str, str_sz, "!policy");
  return 1;
 }

 if (type > policy->max_attr) {
  if (*n < str_sz)
   *n += snprintf(str, str_sz, "!oob");
  return 1;
 }

 if (!policy->table[type].name) {
  if (*n < str_sz)
   *n += snprintf(str, str_sz, "!name");
  return 1;
 }

 if (*n < str_sz) {
  int sz;

  sz = snprintf(str, str_sz - *n,
         ".%s", policy->table[type].name);
  *n += sz;
  str += sz;
 }

 if (policy->table[type].is_submsg) {
  if (!selector) {
   if (*n < str_sz)
    *n += snprintf(str, str_sz, "(!selector)");
   return 1;
  }

  if (ynl_attr_type(selector) !=
      policy->table[type].selector_type) {
   if (*n < str_sz)
    *n += snprintf(str, str_sz, "(!=selector)");
   return 1;
  }

  if (*n < str_sz)
   *n += snprintf(str, str_sz - *n, "(%s)",
           ynl_attr_get_str(selector));
 }

 return 0;
}

static int
ynl_err_walk(struct ynl_sock *ys, void *start, void *end, unsigned int off,
      const struct ynl_policy_nest *policy, char *str, int str_sz,
      const struct ynl_policy_nest **nest_pol)
{
 const struct ynl_policy_nest *next_pol;
 const struct nlattr *selector = NULL;
 unsigned int astart_off, aend_off;
 const struct nlattr *attr;
 unsigned int data_len;
 unsigned int type;
 bool found = false;
 int n = 0;

 if (!policy) {
  if (n < str_sz)
   n += snprintf(str, str_sz, "!policy");
  return n;
 }

 data_len = end - start;

 ynl_attr_for_each_payload(start, data_len, attr) {
  astart_off = (char *)attr - (char *)start;
  aend_off = (char *)ynl_attr_data_end(attr) - (char *)start;

  if (ynl_err_walk_is_sel(policy, attr))
   selector = attr;

  if (aend_off <= off)
   continue;

  found = true;
  break;
 }
 if (!found)
  return 0;

 off -= astart_off;

 type = ynl_attr_type(attr);

 if (ynl_err_walk_report_one(policy, selector, type, str, str_sz, &n))
  return n;

 next_pol = ynl_err_walk_sel_policy(&policy->table[type], selector);
 if (!next_pol)
  return n;

 if (!off) {
  if (nest_pol)
   *nest_pol = next_pol;
  return n;
 }

 if (!next_pol) {
  if (n < str_sz)
   n += snprintf(str, str_sz, "!nest");
  return n;
 }

 off -= sizeof(struct nlattr);
 start =  ynl_attr_data(attr);
 end = start + ynl_attr_data_len(attr);

 return n + ynl_err_walk(ys, start, end, off, next_pol,
    &str[n], str_sz - n, nest_pol);
}

#define NLMSGERR_ATTR_MISS_TYPE (NLMSGERR_ATTR_POLICY + 1)
#define NLMSGERR_ATTR_MISS_NEST (NLMSGERR_ATTR_POLICY + 2)
#define NLMSGERR_ATTR_MAX (NLMSGERR_ATTR_MAX + 2)

static int
ynl_ext_ack_check(struct ynl_sock *ys, const struct nlmsghdr *nlh,
    unsigned int hlen)
{
 const struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
 char miss_attr[sizeof(ys->err.msg)];
 char bad_attr[sizeof(ys->err.msg)];
 const struct nlattr *attr;
 const char *str = NULL;

 if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) {
  yerr_msg(ys, "%s", strerror(ys->err.code));
  return YNL_PARSE_CB_OK;
 }

 ynl_attr_for_each(attr, nlh, hlen) {
  unsigned int len, type;

  len = ynl_attr_data_len(attr);
  type = ynl_attr_type(attr);

  if (type > NLMSGERR_ATTR_MAX)
   continue;

  tb[type] = attr;

  switch (type) {
  case NLMSGERR_ATTR_OFFS:
  case NLMSGERR_ATTR_MISS_TYPE:
  case NLMSGERR_ATTR_MISS_NEST:
   if (len != sizeof(__u32))
    return YNL_PARSE_CB_ERROR;
   break;
  case NLMSGERR_ATTR_MSG:
   str = ynl_attr_get_str(attr);
   if (str[len - 1])
    return YNL_PARSE_CB_ERROR;
   break;
  default:
   break;
  }
 }

 bad_attr[0] = '\0';
 miss_attr[0] = '\0';

 if (tb[NLMSGERR_ATTR_OFFS]) {
  unsigned int n, off;
  void *start, *end;

  ys->err.attr_offs = ynl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);

  n = snprintf(bad_attr, sizeof(bad_attr), "%sbad attribute: ",
        str ? " (" : "");

  start = ynl_nlmsg_data_offset(ys->nlh, ys->req_hdr_len);
  end = ynl_nlmsg_end_addr(ys->nlh);

  off = ys->err.attr_offs;
  off -= sizeof(struct nlmsghdr);
  off -= ys->req_hdr_len;

  n += ynl_err_walk(ys, start, end, off, ys->req_policy,
      &bad_attr[n], sizeof(bad_attr) - n, NULL);

  if (n >= sizeof(bad_attr))
   n = sizeof(bad_attr) - 1;
  bad_attr[n] = '\0';
 }
 if (tb[NLMSGERR_ATTR_MISS_TYPE]) {
  const struct ynl_policy_nest *nest_pol = NULL;
  unsigned int n, off, type;
  void *start, *end;
  int n2;

  type = ynl_attr_get_u32(tb[NLMSGERR_ATTR_MISS_TYPE]);

  n = snprintf(miss_attr, sizeof(miss_attr), "%smissing attribute: ",
        bad_attr[0] ? ", " : (str ? " (" : ""));

  start = ynl_nlmsg_data_offset(ys->nlh, ys->req_hdr_len);
  end = ynl_nlmsg_end_addr(ys->nlh);

  nest_pol = ys->req_policy;
  if (tb[NLMSGERR_ATTR_MISS_NEST]) {
   off = ynl_attr_get_u32(tb[NLMSGERR_ATTR_MISS_NEST]);
   off -= sizeof(struct nlmsghdr);
   off -= ys->req_hdr_len;

   n += ynl_err_walk(ys, start, end, off, ys->req_policy,
       &miss_attr[n], sizeof(miss_attr) - n,
       &nest_pol);
  }

  n2 = 0;
  ynl_err_walk_report_one(nest_pol, NULL, type, &miss_attr[n],
     sizeof(miss_attr) - n, &n2);
  n += n2;

  if (n >= sizeof(miss_attr))
   n = sizeof(miss_attr) - 1;
  miss_attr[n] = '\0';
 }

 /* Implicitly depend on ys->err.code already set */
 if (str)
  yerr_msg(ys, "Kernel %s: '%s'%s%s%s",
    ys->err.code ? "error" : "warning",
    str, bad_attr, miss_attr,
    bad_attr[0] || miss_attr[0] ? ")" : "");
 else if (bad_attr[0] || miss_attr[0])
  yerr_msg(ys, "Kernel %s: %s%s",
    ys->err.code ? "error" : "warning",
    bad_attr, miss_attr);
 else
  yerr_msg(ys, "%s", strerror(ys->err.code));

 return YNL_PARSE_CB_OK;
}

static int
ynl_cb_error(const struct nlmsghdr *nlh, struct ynl_parse_arg *yarg)
{
 const struct nlmsgerr *err = ynl_nlmsg_data(nlh);
 unsigned int hlen;
 int code;

 code = err->error >= 0 ? err->error : -err->error;
 yarg->ys->err.code = code;
 errno = code;

 hlen = sizeof(*err);
 if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
  hlen += ynl_nlmsg_data_len(&err->msg);

 ynl_ext_ack_check(yarg->ys, nlh, hlen);

 return code ? YNL_PARSE_CB_ERROR : YNL_PARSE_CB_STOP;
}

static int ynl_cb_done(const struct nlmsghdr *nlh, struct ynl_parse_arg *yarg)
{
 int err;

 err = *(int *)NLMSG_DATA(nlh);
 if (err < 0) {
  yarg->ys->err.code = -err;
  errno = -err;

  ynl_ext_ack_check(yarg->ys, nlh, sizeof(int));

  return YNL_PARSE_CB_ERROR;
 }
 return YNL_PARSE_CB_STOP;
}

/* Attribute validation */

int __ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr,
   unsigned int type)
{
 const struct ynl_policy_attr *policy;
 unsigned char *data;
 unsigned int len;

 data = ynl_attr_data(attr);
 len = ynl_attr_data_len(attr);
 if (type > yarg->rsp_policy->max_attr) {
  yerr(yarg->ys, YNL_ERROR_INTERNAL,
       "Internal error, validating unknown attribute");
  return -1;
 }

 policy = &yarg->rsp_policy->table[type];

 switch (policy->type) {
 case YNL_PT_REJECT:
  yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
       "Rejected attribute (%s)", policy->name);
  return -1;
 case YNL_PT_IGNORE:
  break;
 case YNL_PT_U8:
  if (len == sizeof(__u8))
   break;
  yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
       "Invalid attribute (u8 %s)", policy->name);
  return -1;
 case YNL_PT_U16:
  if (len == sizeof(__u16))
   break;
  yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
       "Invalid attribute (u16 %s)", policy->name);
  return -1;
 case YNL_PT_U32:
  if (len == sizeof(__u32))
   break;
  yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
       "Invalid attribute (u32 %s)", policy->name);
  return -1;
 case YNL_PT_U64:
  if (len == sizeof(__u64))
   break;
  yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
       "Invalid attribute (u64 %s)", policy->name);
  return -1;
 case YNL_PT_UINT:
  if (len == sizeof(__u32) || len == sizeof(__u64))
   break;
  yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
       "Invalid attribute (uint %s)", policy->name);
  return -1;
 case YNL_PT_FLAG:
  /* Let flags grow into real attrs, why not.. */
  break;
 case YNL_PT_NEST:
  if (!len || len >= sizeof(*attr))
   break;
  yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
       "Invalid attribute (nest %s)", policy->name);
  return -1;
 case YNL_PT_BINARY:
  if (!policy->len || len == policy->len)
   break;
  yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
       "Invalid attribute (binary %s)", policy->name);
  return -1;
 case YNL_PT_NUL_STR:
  if (len && (!policy->len || len <= policy->len) && !data[len - 1])
   break;
  yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
       "Invalid attribute (string %s)", policy->name);
  return -1;
 case YNL_PT_BITFIELD32:
  if (len == sizeof(struct nla_bitfield32))
   break;
  yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
       "Invalid attribute (bitfield32 %s)", policy->name);
  return -1;
 default:
  yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
       "Invalid attribute (unknown %s)", policy->name);
  return -1;
 }

 return 0;
}

int ynl_submsg_failed(struct ynl_parse_arg *yarg, const char *field_name,
        const char *sel_name)
{
 yerr(yarg->ys, YNL_ERROR_SUBMSG_KEY,
      "Parsing error: Sub-message key not set (msg %s, key %s)",
      field_name, sel_name);
 return YNL_PARSE_CB_ERROR;
}

/* Generic code */

static void ynl_err_reset(struct ynl_sock *ys)
{
 ys->err.code = 0;
 ys->err.attr_offs = 0;
 ys->err.msg[0] = 0;
}

struct nlmsghdr *ynl_msg_start(struct ynl_sock *ys, __u32 id, __u16 flags)
{
 struct nlmsghdr *nlh;

 ynl_err_reset(ys);

 nlh = ys->nlh = ynl_nlmsg_put_header(ys->tx_buf);
 nlh->nlmsg_type = id;
 nlh->nlmsg_flags = flags;
 nlh->nlmsg_seq = ++ys->seq;

 /* This is a local YNL hack for length checking, we put the buffer
 * length in nlmsg_pid, since messages sent to the kernel always use
 * PID 0. Message needs to be terminated with ynl_msg_end().
 */

 nlh->nlmsg_pid = YNL_SOCKET_BUFFER_SIZE;

 return nlh;
}

static int ynl_msg_end(struct ynl_sock *ys, struct nlmsghdr *nlh)
{
 /* We stash buffer length in nlmsg_pid. */
 if (nlh->nlmsg_pid == 0) {
  yerr(ys, YNL_ERROR_INPUT_INVALID,
       "Unknown input buffer length");
  return -EINVAL;
 }
 if (nlh->nlmsg_pid == YNL_MSG_OVERFLOW) {
  yerr(ys, YNL_ERROR_INPUT_TOO_BIG,
       "Constructed message longer than internal buffer");
  return -EMSGSIZE;
 }

 nlh->nlmsg_pid = 0;
 return 0;
}

struct nlmsghdr *
ynl_gemsg_start(struct ynl_sock *ys, __u32 id, __u16 flags,
  __u8 cmd, __u8 version)
{
 struct genlmsghdr gehdr;
 struct nlmsghdr *nlh;
 void *data;

 nlh = ynl_msg_start(ys, id, flags);

 memset(&gehdr, 0, sizeof(gehdr));
 gehdr.cmd = cmd;
 gehdr.version = version;

 data = ynl_nlmsg_put_extra_header(nlh, sizeof(gehdr));
 memcpy(data, &gehdr, sizeof(gehdr));

 return nlh;
}

struct nlmsghdr *ynl_msg_start_req(struct ynl_sock *ys, __u32 id, __u16 flags)
{
 return ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | flags);
}

struct nlmsghdr *ynl_msg_start_dump(struct ynl_sock *ys, __u32 id)
{
 return ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
}

struct nlmsghdr *
ynl_gemsg_start_req(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version)
{
 return ynl_gemsg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK, cmd, version);
}

struct nlmsghdr *
ynl_gemsg_start_dump(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version)
{
 return ynl_gemsg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP,
          cmd, version);
}

static int ynl_cb_null(const struct nlmsghdr *nlh, struct ynl_parse_arg *yarg)
{
 yerr(yarg->ys, YNL_ERROR_UNEXPECT_MSG,
      "Received a message when none were expected");

 return YNL_PARSE_CB_ERROR;
}

static int
__ynl_sock_read_msgs(struct ynl_parse_arg *yarg, ynl_parse_cb_t cb, int flags)
{
 struct ynl_sock *ys = yarg->ys;
 const struct nlmsghdr *nlh;
 ssize_t len, rem;
 int ret;

 len = recv(ys->socket, ys->rx_buf, YNL_SOCKET_BUFFER_SIZE, flags);
 if (len < 0) {
  if (flags & MSG_DONTWAIT && errno == EAGAIN)
   return YNL_PARSE_CB_STOP;
  return len;
 }

 ret = YNL_PARSE_CB_STOP;
 for (rem = len; rem > 0; NLMSG_NEXT(nlh, rem)) {
  nlh = (struct nlmsghdr *)&ys->rx_buf[len - rem];
  if (!NLMSG_OK(nlh, rem)) {
   yerr(yarg->ys, YNL_ERROR_INV_RESP,
        "Invalid message or trailing data in the response.");
   return YNL_PARSE_CB_ERROR;
  }

  if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
   /* TODO: handle this better */
   yerr(yarg->ys, YNL_ERROR_DUMP_INTER,
        "Dump interrupted / inconsistent, please retry.");
   return YNL_PARSE_CB_ERROR;
  }

  switch (nlh->nlmsg_type) {
  case 0:
   yerr(yarg->ys, YNL_ERROR_INV_RESP,
        "Invalid message type in the response.");
   return YNL_PARSE_CB_ERROR;
  case NLMSG_NOOP:
  case NLMSG_OVERRUN ... NLMSG_MIN_TYPE - 1:
   ret = YNL_PARSE_CB_OK;
   break;
  case NLMSG_ERROR:
   ret = ynl_cb_error(nlh, yarg);
   break;
  case NLMSG_DONE:
   ret = ynl_cb_done(nlh, yarg);
   break;
  default:
   ret = cb(nlh, yarg);
   break;
  }
 }

 return ret;
}

static int ynl_sock_read_msgs(struct ynl_parse_arg *yarg, ynl_parse_cb_t cb)
{
 return __ynl_sock_read_msgs(yarg, cb, 0);
}

static int ynl_recv_ack(struct ynl_sock *ys, int ret)
{
 struct ynl_parse_arg yarg = { .ys = ys, };

 if (!ret) {
  yerr(ys, YNL_ERROR_EXPECT_ACK,
       "Expecting an ACK but nothing received");
  return -1;
 }

 return ynl_sock_read_msgs(&yarg, ynl_cb_null);
}

/* Init/fini and genetlink boiler plate */
static int
ynl_get_family_info_mcast(struct ynl_sock *ys, const struct nlattr *mcasts)
{
 const struct nlattr *entry, *attr;
 unsigned int i;

 ynl_attr_for_each_nested(attr, mcasts)
  ys->n_mcast_groups++;

 if (!ys->n_mcast_groups)
  return 0;

 ys->mcast_groups = calloc(ys->n_mcast_groups,
      sizeof(*ys->mcast_groups));
 if (!ys->mcast_groups)
  return YNL_PARSE_CB_ERROR;

 i = 0;
 ynl_attr_for_each_nested(entry, mcasts) {
  ynl_attr_for_each_nested(attr, entry) {
   if (ynl_attr_type(attr) == CTRL_ATTR_MCAST_GRP_ID)
    ys->mcast_groups[i].id = ynl_attr_get_u32(attr);
   if (ynl_attr_type(attr) == CTRL_ATTR_MCAST_GRP_NAME) {
    strncpy(ys->mcast_groups[i].name,
     ynl_attr_get_str(attr),
     GENL_NAMSIZ - 1);
    ys->mcast_groups[i].name[GENL_NAMSIZ - 1] = 0;
   }
  }
  i++;
 }

 return 0;
}

static int
ynl_get_family_info_cb(const struct nlmsghdr *nlh, struct ynl_parse_arg *yarg)
{
 struct ynl_sock *ys = yarg->ys;
 const struct nlattr *attr;
 bool found_id = true;

 ynl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) {
  if (ynl_attr_type(attr) == CTRL_ATTR_MCAST_GROUPS)
   if (ynl_get_family_info_mcast(ys, attr))
    return YNL_PARSE_CB_ERROR;

  if (ynl_attr_type(attr) != CTRL_ATTR_FAMILY_ID)
   continue;

  if (ynl_attr_data_len(attr) != sizeof(__u16)) {
   yerr(ys, YNL_ERROR_ATTR_INVALID, "Invalid family ID");
   return YNL_PARSE_CB_ERROR;
  }

  ys->family_id = ynl_attr_get_u16(attr);
  found_id = true;
 }

 if (!found_id) {
  yerr(ys, YNL_ERROR_ATTR_MISSING, "Family ID missing");
  return YNL_PARSE_CB_ERROR;
 }
 return YNL_PARSE_CB_OK;
}

static int ynl_sock_read_family(struct ynl_sock *ys, const char *family_name)
{
 struct ynl_parse_arg yarg = { .ys = ys, };
 struct nlmsghdr *nlh;
 int err;

 nlh = ynl_gemsg_start_req(ys, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 1);
 ynl_attr_put_str(nlh, CTRL_ATTR_FAMILY_NAME, family_name);

 err = ynl_msg_end(ys, nlh);
 if (err < 0)
  return err;

 err = send(ys->socket, nlh, nlh->nlmsg_len, 0);
 if (err < 0) {
  perr(ys, "failed to request socket family info");
  return err;
 }

 err = ynl_sock_read_msgs(&yarg, ynl_get_family_info_cb);
 if (err < 0) {
  free(ys->mcast_groups);
  perr(ys, "failed to receive the socket family info - no such family?");
  return err;
 }

 err = ynl_recv_ack(ys, err);
 if (err < 0) {
  free(ys->mcast_groups);
  return err;
 }

 return 0;
}

struct ynl_sock *
ynl_sock_create(const struct ynl_family *yf, struct ynl_error *yse)
{
 struct sockaddr_nl addr;
 struct ynl_sock *ys;
 socklen_t addrlen;
 int sock_type;
 int one = 1;

 ys = malloc(sizeof(*ys) + 2 * YNL_SOCKET_BUFFER_SIZE);
 if (!ys)
  return NULL;
 memset(ys, 0, sizeof(*ys));

 ys->family = yf;
 ys->tx_buf = &ys->raw_buf[0];
 ys->rx_buf = &ys->raw_buf[YNL_SOCKET_BUFFER_SIZE];
 ys->ntf_last_next = &ys->ntf_first;

 sock_type = yf->is_classic ? yf->classic_id : NETLINK_GENERIC;

 ys->socket = socket(AF_NETLINK, SOCK_RAW, sock_type);
 if (ys->socket < 0) {
  __perr(yse, "failed to create a netlink socket");
  goto err_free_sock;
 }

 if (setsockopt(ys->socket, SOL_NETLINK, NETLINK_CAP_ACK,
         &one, sizeof(one))) {
  __perr(yse, "failed to enable netlink ACK");
  goto err_close_sock;
 }
 if (setsockopt(ys->socket, SOL_NETLINK, NETLINK_EXT_ACK,
         &one, sizeof(one))) {
  __perr(yse, "failed to enable netlink ext ACK");
  goto err_close_sock;
 }

 memset(&addr, 0, sizeof(addr));
 addr.nl_family = AF_NETLINK;
 if (bind(ys->socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
  __perr(yse, "unable to bind to a socket address");
  goto err_close_sock;
 }

 memset(&addr, 0, sizeof(addr));
 addrlen = sizeof(addr);
 if (getsockname(ys->socket, (struct sockaddr *)&addr, &addrlen) < 0) {
  __perr(yse, "unable to read socket address");
  goto err_close_sock;
 }
 ys->portid = addr.nl_pid;
 ys->seq = random();

 if (yf->is_classic) {
  ys->family_id = yf->classic_id;
 } else if (ynl_sock_read_family(ys, yf->name)) {
  if (yse)
   memcpy(yse, &ys->err, sizeof(*yse));
  goto err_close_sock;
 }

 return ys;

err_close_sock:
 close(ys->socket);
err_free_sock:
 free(ys);
 return NULL;
}

void ynl_sock_destroy(struct ynl_sock *ys)
{
 struct ynl_ntf_base_type *ntf;

 close(ys->socket);
 while ((ntf = ynl_ntf_dequeue(ys)))
  ynl_ntf_free(ntf);
 free(ys->mcast_groups);
 free(ys);
}

/* YNL multicast handling */

void ynl_ntf_free(struct ynl_ntf_base_type *ntf)
{
 ntf->free(ntf);
}

int ynl_subscribe(struct ynl_sock *ys, const char *grp_name)
{
 unsigned int i;
 int err;

 for (i = 0; i < ys->n_mcast_groups; i++)
  if (!strcmp(ys->mcast_groups[i].name, grp_name))
   break;
 if (i == ys->n_mcast_groups) {
  yerr(ys, ENOENT, "Multicast group '%s' not found", grp_name);
  return -1;
 }

 err = setsockopt(ys->socket, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
    &ys->mcast_groups[i].id,
    sizeof(ys->mcast_groups[i].id));
 if (err < 0) {
  perr(ys, "Subscribing to multicast group failed");
  return -1;
 }

 return 0;
}

int ynl_socket_get_fd(struct ynl_sock *ys)
{
 return ys->socket;
}

struct ynl_ntf_base_type *ynl_ntf_dequeue(struct ynl_sock *ys)
{
 struct ynl_ntf_base_type *ntf;

 if (!ynl_has_ntf(ys))
  return NULL;

 ntf = ys->ntf_first;
 ys->ntf_first = ntf->next;
 if (ys->ntf_last_next == &ntf->next)
  ys->ntf_last_next = &ys->ntf_first;

 return ntf;
}

static int ynl_ntf_parse(struct ynl_sock *ys, const struct nlmsghdr *nlh)
{
 struct ynl_parse_arg yarg = { .ys = ys, };
 const struct ynl_ntf_info *info;
 struct ynl_ntf_base_type *rsp;
 __u32 cmd;
 int ret;

 if (ys->family->is_classic) {
  cmd = nlh->nlmsg_type;
 } else {
  struct genlmsghdr *gehdr;

  gehdr = ynl_nlmsg_data(nlh);
  cmd = gehdr->cmd;
 }

 if (cmd >= ys->family->ntf_info_size)
  return YNL_PARSE_CB_ERROR;
 info = &ys->family->ntf_info[cmd];
 if (!info->cb)
  return YNL_PARSE_CB_ERROR;

 rsp = calloc(1, info->alloc_sz);
 rsp->free = info->free;
 yarg.data = rsp->data;
 yarg.rsp_policy = info->policy;

 ret = info->cb(nlh, &yarg);
 if (ret <= YNL_PARSE_CB_STOP)
  goto err_free;

 rsp->family = nlh->nlmsg_type;
 rsp->cmd = cmd;

 *ys->ntf_last_next = rsp;
 ys->ntf_last_next = &rsp->next;

 return YNL_PARSE_CB_OK;

err_free:
 info->free(rsp);
 return YNL_PARSE_CB_ERROR;
}

static int
ynl_ntf_trampoline(const struct nlmsghdr *nlh, struct ynl_parse_arg *yarg)
{
 return ynl_ntf_parse(yarg->ys, nlh);
}

int ynl_ntf_check(struct ynl_sock *ys)
{
 struct ynl_parse_arg yarg = { .ys = ys, };
 int err;

 do {
  err = __ynl_sock_read_msgs(&yarg, ynl_ntf_trampoline,
        MSG_DONTWAIT);
  if (err < 0)
   return err;
 } while (err > 0);

 return 0;
}

/* YNL specific helpers used by the auto-generated code */

struct ynl_dump_list_type *YNL_LIST_END = (void *)(0xb4d123);

void ynl_error_unknown_notification(struct ynl_sock *ys, __u8 cmd)
{
 yerr(ys, YNL_ERROR_UNKNOWN_NTF,
      "Unknown notification message type '%d'", cmd);
}

int ynl_error_parse(struct ynl_parse_arg *yarg, const char *msg)
{
 yerr(yarg->ys, YNL_ERROR_INV_RESP, "Error parsing response: %s", msg);
 return YNL_PARSE_CB_ERROR;
}

static int
ynl_check_alien(struct ynl_sock *ys, const struct nlmsghdr *nlh, __u32 rsp_cmd)
{
 if (ys->family->is_classic) {
  if (nlh->nlmsg_type != rsp_cmd)
   return ynl_ntf_parse(ys, nlh);
 } else {
  struct genlmsghdr *gehdr;

  if (ynl_nlmsg_data_len(nlh) < sizeof(*gehdr)) {
   yerr(ys, YNL_ERROR_INV_RESP,
        "Kernel responded with truncated message");
   return -1;
  }

  gehdr = ynl_nlmsg_data(nlh);
  if (gehdr->cmd != rsp_cmd)
   return ynl_ntf_parse(ys, nlh);
 }

 return 0;
}

static
int ynl_req_trampoline(const struct nlmsghdr *nlh, struct ynl_parse_arg *yarg)
{
 struct ynl_req_state *yrs = (void *)yarg;
 int ret;

 ret = ynl_check_alien(yrs->yarg.ys, nlh, yrs->rsp_cmd);
 if (ret)
  return ret < 0 ? YNL_PARSE_CB_ERROR : YNL_PARSE_CB_OK;

 return yrs->cb(nlh, &yrs->yarg);
}

int ynl_exec(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
      struct ynl_req_state *yrs)
{
 int err;

 err = ynl_msg_end(ys, req_nlh);
 if (err < 0)
  return err;

 err = send(ys->socket, req_nlh, req_nlh->nlmsg_len, 0);
 if (err < 0)
  return err;

 do {
  err = ynl_sock_read_msgs(&yrs->yarg, ynl_req_trampoline);
 } while (err > 0);

 return err;
}

static int
ynl_dump_trampoline(const struct nlmsghdr *nlh, struct ynl_parse_arg *data)
{
 struct ynl_dump_state *ds = (void *)data;
 struct ynl_dump_list_type *obj;
 struct ynl_parse_arg yarg = {};
 int ret;

 ret = ynl_check_alien(ds->yarg.ys, nlh, ds->rsp_cmd);
 if (ret)
  return ret < 0 ? YNL_PARSE_CB_ERROR : YNL_PARSE_CB_OK;

 obj = calloc(1, ds->alloc_sz);
 if (!obj)
  return YNL_PARSE_CB_ERROR;

 if (!ds->first)
  ds->first = obj;
 if (ds->last)
  ds->last->next = obj;
 ds->last = obj;

 yarg = ds->yarg;
 yarg.data = &obj->data;

 return ds->cb(nlh, &yarg);
}

static void *ynl_dump_end(struct ynl_dump_state *ds)
{
 if (!ds->first)
  return YNL_LIST_END;

 ds->last->next = YNL_LIST_END;
 return ds->first;
}

int ynl_exec_dump(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
    struct ynl_dump_state *yds)
{
 int err;

 err = ynl_msg_end(ys, req_nlh);
 if (err < 0)
  return err;

 err = send(ys->socket, req_nlh, req_nlh->nlmsg_len, 0);
 if (err < 0)
  return err;

 do {
  err = ynl_sock_read_msgs(&yds->yarg, ynl_dump_trampoline);
  if (err < 0)
   goto err_close_list;
 } while (err > 0);

 yds->first = ynl_dump_end(yds);
 return 0;

err_close_list:
 yds->first = ynl_dump_end(yds);
 return -1;
}

Messung V0.5
C=99 H=95 G=96

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.