/* * event.c - implements event subscription and handling APIs * * Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved. * * Author: anjan.chanda@iopsys.eu * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> #include <netlink/genl/ctrl.h> #include <netlink/genl/genl.h> #include <netlink/route/link.h> #include <netlink/attr.h> #include "easy.h" #define MAX_MSG_LEN 512 struct default_event { struct nl_sock *sock; struct event_struct req; int (*filter_event)(struct event_struct *req, char *resp_data); }; /* default_event attributes */ enum default_event_attrs { DEFAULT_EVENT_ATTR_UNSPEC, DEFAULT_EVENT_ATTR_MSG, __DEFAULT_EVENT_ATTR_AFTER_LAST, NUM_DEFAULT_EVENT_ATTR = __DEFAULT_EVENT_ATTR_AFTER_LAST, DEFAULT_EVENT_ATTR_MAX = __DEFAULT_EVENT_ATTR_AFTER_LAST - 1, }; /* default_event policy */ static struct nla_policy default_event_policy[NUM_DEFAULT_EVENT_ATTR] = { [DEFAULT_EVENT_ATTR_MSG] = { .type = NLA_STRING }, }; int easy_default_event_handler(struct nl_msg *msg, void *arg) { struct default_event *ev = (struct default_event *)arg; struct event_response *resp = &ev->req.resp; struct nlmsghdr *nlh = nlmsg_hdr(msg); struct nlattr *attrs[NUM_DEFAULT_EVENT_ATTR]; int ret; if (!genlmsg_valid_hdr(nlh, 0)) { libeasy_err("received invalid message\n"); return 0; } ret = genlmsg_parse(nlh, 0, attrs, DEFAULT_EVENT_ATTR_MSG, default_event_policy); if (ret) return ret; /* TODO: match 'ev->req.ifname' with received event's ifname and * discard events that are not ours. * This will need change in the default_event_attrs to include an * attribute for 'ifname'. And, all event senders MUST fill that * attribute with appropriate 'ifname'. * * Until that is done, we find "vif" string's value in the event * message string. */ if (attrs[DEFAULT_EVENT_ATTR_MSG]) { size_t len = (size_t)nla_len(attrs[DEFAULT_EVENT_ATTR_MSG]); if (len > MAX_MSG_LEN) { /* event will be discarded by caller anyway * if ill-formed. */ len = MAX_MSG_LEN; } resp->type = EASY_EVENT_DEFAULT; resp->len = (uint32_t)len; if (resp->data) { memcpy(resp->data, nla_get_string(attrs[DEFAULT_EVENT_ATTR_MSG]), len); } /* Allow caller to filter events before its callback * gets called. * Discard event if filter_event() returned non-zero. */ if (ev->filter_event && ev->filter_event(&ev->req, (char *)resp->data)) { return 0; } if (ev->req.cb) ev->req.cb(&ev->req); } return 0; } int LIBEASY_API easy_register_event(struct event_struct *req, void **handle) { struct default_event *ev; struct nl_sock *sock; int grp; int err; ev = calloc(1, sizeof(struct default_event)); if (!ev) { libeasy_err("%s: malloc failed!\n", __func__); return -ENOMEM; } memcpy(&ev->req, req, sizeof(struct event_struct)); sock = nl_socket_alloc(); if (!sock) { libeasy_err("Error: nl_socket_alloc\n"); goto free_handle; } ev->sock = sock; ev->filter_event = req->filter_event; nl_socket_disable_seq_check(sock); nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, easy_default_event_handler, ev); if ((err = genl_connect(sock)) < 0) { libeasy_err("Error: %s\n", nl_geterror(err)); goto free_sock; } if ((grp = genl_ctrl_resolve_grp(sock, req->family, req->group)) < 0) { libeasy_err("Error: %s ('%s', '%s')\n", nl_geterror(grp), req->family, req->group); goto free_sock; } nl_socket_add_membership(sock, grp); req->fd_monitor = easy_get_event_fd(ev); *handle = ev; return 0; free_sock: nl_socket_free(sock); free_handle: free(ev); *handle = NULL; return -1; } int LIBEASY_API easy_unregister_event(void *handle) { struct default_event *ev = (struct default_event *)handle; struct event_struct *req; int ret = -1; int grp; if (!ev) return ret; req = &ev->req; grp = genl_ctrl_resolve_grp(ev->sock, req->family, req->group); if (grp < 0) { libeasy_err("Error: %s ('%s', '%s')\n", nl_geterror(grp), req->family, req->group); goto free_sock; } ret = 0; nl_socket_drop_membership(ev->sock, grp); free_sock: nl_socket_free(ev->sock); free(ev); return ret; } int LIBEASY_API easy_recv_event(void *handle) { struct default_event *ev = (struct default_event *)handle; int err; err = nl_recvmsgs_default(ev->sock); if (err < 0) { libeasy_err("Error: %s\n", nl_geterror(err)); return err; } return 0; } int LIBEASY_API easy_get_event_fd(void *handle) { if (!handle) return -1; return nl_socket_get_fd(((struct default_event *)handle)->sock); }