aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/sflow/sflow_usersock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/sflow/sflow_usersock.c')
-rw-r--r--src/plugins/sflow/sflow_usersock.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/src/plugins/sflow/sflow_usersock.c b/src/plugins/sflow/sflow_usersock.c
new file mode 100644
index 00000000000..0ccb947709a
--- /dev/null
+++ b/src/plugins/sflow/sflow_usersock.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2024 InMon Corp.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+#include <sflow/sflow.h>
+
+#include <fcntl.h>
+#include <asm/types.h>
+#include <sys/socket.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <signal.h>
+#include <ctype.h>
+
+#include <sflow/sflow_usersock.h>
+
+ /*_________________---------------------------__________________
+ _________________ fcntl utils __________________
+ -----------------___________________________------------------
+ */
+
+ static void
+ setNonBlocking (int fd)
+ {
+ // set the socket to non-blocking
+ int fdFlags = fcntl (fd, F_GETFL);
+ fdFlags |= O_NONBLOCK;
+ if (fcntl (fd, F_SETFL, fdFlags) < 0)
+ {
+ SFLOW_ERR ("fcntl(O_NONBLOCK) failed: %s\n", strerror (errno));
+ }
+ }
+
+ static void
+ setCloseOnExec (int fd)
+ {
+ // make sure it doesn't get inherited, e.g. when we fork a script
+ int fdFlags = fcntl (fd, F_GETFD);
+ fdFlags |= FD_CLOEXEC;
+ if (fcntl (fd, F_SETFD, fdFlags) < 0)
+ {
+ SFLOW_ERR ("fcntl(F_SETFD=FD_CLOEXEC) failed: %s\n", strerror (errno));
+ }
+ }
+
+ /*_________________---------------------------__________________
+ _________________ usersock_open __________________
+ -----------------___________________________------------------
+ */
+
+ static int
+ usersock_open (void)
+ {
+ int nl_sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_USERSOCK);
+ if (nl_sock < 0)
+ {
+ SFLOW_ERR ("nl_sock open failed: %s\n", strerror (errno));
+ return -1;
+ }
+ setNonBlocking (nl_sock);
+ setCloseOnExec (nl_sock);
+ return nl_sock;
+ }
+
+ /*_________________---------------------------__________________
+ _________________ SFLOWUS_open __________________
+ -----------------___________________________------------------
+ */
+
+ bool
+ SFLOWUS_open (SFLOWUS *ust)
+ {
+ if (ust->nl_sock == 0)
+ {
+ ust->nl_sock = usersock_open ();
+ }
+ return true;
+ }
+
+ /*_________________---------------------------__________________
+ _________________ SFLOWUS_close __________________
+ -----------------___________________________------------------
+ */
+
+ bool
+ SFLOWUS_close (SFLOWUS *ust)
+ {
+ if (ust->nl_sock != 0)
+ {
+ int err = close (ust->nl_sock);
+ if (err == 0)
+ {
+ ust->nl_sock = 0;
+ return true;
+ }
+ else
+ {
+ SFLOW_WARN ("SFLOWUS_close: returned %d : %s\n", err,
+ strerror (errno));
+ }
+ }
+ return false;
+ }
+
+ /*_________________---------------------------__________________
+ _________________ SFLOWUSSpec_setMsgType __________________
+ -----------------___________________________------------------
+ */
+
+ bool
+ SFLOWUSSpec_setMsgType (SFLOWUSSpec *spec, EnumSFlowVppMsgType msgType)
+ {
+ spec->nlh.nlmsg_type = msgType;
+ return true;
+ }
+
+ /*_________________---------------------------__________________
+ _________________ SFLOWUSSpec_setAttr __________________
+ -----------------___________________________------------------
+ */
+
+ bool
+ SFLOWUSSpec_setAttr (SFLOWUSSpec *spec, EnumSFlowVppAttributes field,
+ void *val, int len)
+ {
+ SFLOWUSAttr *usa = &spec->attr[field];
+ if (usa->included)
+ return false;
+ usa->included = true;
+ usa->attr.nla_type = field;
+ usa->attr.nla_len = sizeof (usa->attr) + len;
+ int len_w_pad = NLMSG_ALIGN (len);
+ usa->val.iov_len = len_w_pad;
+ usa->val.iov_base = val;
+ spec->n_attrs++;
+ spec->attrs_len += sizeof (usa->attr);
+ spec->attrs_len += len_w_pad;
+ return true;
+ }
+
+ /*_________________---------------------------__________________
+ _________________ SFLOWUSSpec_send __________________
+ -----------------___________________________------------------
+ */
+
+ int
+ SFLOWUSSpec_send (SFLOWUS *ust, SFLOWUSSpec *spec)
+ {
+ spec->nlh.nlmsg_len = NLMSG_LENGTH (spec->attrs_len);
+ spec->nlh.nlmsg_flags = 0;
+ spec->nlh.nlmsg_seq = ++ust->nl_seq;
+ spec->nlh.nlmsg_pid = getpid ();
+
+#define MAX_IOV_FRAGMENTS (2 * __SFLOW_VPP_ATTR_MAX) + 2
+
+ struct iovec iov[MAX_IOV_FRAGMENTS];
+ u32 frag = 0;
+ iov[frag].iov_base = &spec->nlh;
+ iov[frag].iov_len = sizeof (spec->nlh);
+ frag++;
+ int nn = 0;
+ for (u32 ii = 0; ii < __SFLOW_VPP_ATTR_MAX; ii++)
+ {
+ SFLOWUSAttr *usa = &spec->attr[ii];
+ if (usa->included)
+ {
+ nn++;
+ iov[frag].iov_base = &usa->attr;
+ iov[frag].iov_len = sizeof (usa->attr);
+ frag++;
+ iov[frag] = usa->val; // struct copy
+ frag++;
+ }
+ }
+ ASSERT (nn == spec->n_attrs);
+
+ struct sockaddr_nl da = {
+ .nl_family = AF_NETLINK,
+ .nl_groups = (1 << (ust->group_id - 1)) // for multicast to the group
+ // .nl_pid = 1e9+6343 // for unicast to receiver bound to netlink socket
+ // with that "pid"
+ };
+
+ struct msghdr msg = { .msg_name = &da,
+ .msg_namelen = sizeof (da),
+ .msg_iov = iov,
+ .msg_iovlen = frag };
+
+ int status = sendmsg (ust->nl_sock, &msg, 0);
+ if (status <= 0)
+ {
+ // Linux replies with ECONNREFUSED when
+ // a multicast is sent via NETLINK_USERSOCK, but
+ // it's not an error so we can just ignore it here.
+ if (errno != ECONNREFUSED)
+ {
+ SFLOW_DBG ("USERSOCK strerror(errno) = %s\n", strerror (errno));
+ return -1;
+ }
+ }
+ return 0;
+ }