From 4ec7371233268f267a4c219eebe193b0efff9bd0 Mon Sep 17 00:00:00 2001 From: Marcel Moolenaar Date: Mon, 18 Aug 2014 23:45:40 +0000 Subject: [PATCH] For vendors like Juniper, extensibility for sockets is important. A good example is socket options that aren't necessarily generic. To this end, OSD is added to the socket structure and hooks are defined for key operations on sockets. These are: o soalloc() and sodealloc() o Get and set socket options o Socket related kevent filters. One aspect about hhook that appears to be not fully baked is the return semantics (the return value from the hook is ignored in hhook_run_hooks() at the time of commit). To support return values, the socket_hhook_data structure contains a 'status' field to hold return values. Submitted by: Anuranjan Shukla Obtained from: Juniper Networks, Inc. --- sys/kern/uipc_socket.c | 91 +++++++++++++++++++++++++++++++++++++++--- sys/sys/hhook.h | 1 + sys/sys/khelp.h | 1 + sys/sys/socketvar.h | 22 ++++++++++ 4 files changed, 109 insertions(+), 6 deletions(-) diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index ddf52b08c90..7ff7b408ad8 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -118,7 +118,9 @@ __FBSDID("$FreeBSD$"); #include #include #include /* for struct knote */ +#include #include +#include #include #include #include @@ -157,6 +159,7 @@ static int filt_soread(struct knote *kn, long hint); static void filt_sowdetach(struct knote *kn); static int filt_sowrite(struct knote *kn, long hint); static int filt_solisten(struct knote *kn, long hint); +static int inline hhook_run_socket(struct socket *so, void *hctx, int32_t h_id); static struct filterops solisten_filtops = { .f_isfd = 1, @@ -183,6 +186,9 @@ MALLOC_DEFINE(M_PCB, "pcb", "protocol control block"); VNET_ASSERT(curvnet != NULL, \ ("%s:%d curvnet is NULL, so=%p", __func__, __LINE__, (so))); +VNET_DEFINE(struct hhook_head *, socket_hhh[HHOOK_SOCKET_LAST + 1]); +#define V_socket_hhh VNET(socket_hhh) + /* * Limit on the number of connections in the listen queue waiting * for accept(2). @@ -254,9 +260,20 @@ socket_zone_change(void *tag) maxsockets = uma_zone_set_max(socket_zone, maxsockets); } +static void +socket_hhook_register(int subtype) +{ + + if (hhook_head_register(HHOOK_TYPE_SOCKET, subtype, + &V_socket_hhh[subtype], + HHOOK_NOWAIT|HHOOK_HEADISINVNET) != 0) + printf("%s: WARNING: unable to register hook\n", __func__); +} + static void socket_init(void *tag) { + int i; socket_zone = uma_zcreate("socket", sizeof(struct socket), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); @@ -264,6 +281,11 @@ socket_init(void *tag) uma_zone_set_warning(socket_zone, "kern.ipc.maxsockets limit reached"); EVENTHANDLER_REGISTER(maxsockets_change, socket_zone_change, NULL, EVENTHANDLER_PRI_FIRST); + + /* We expect a contiguous range */ + for (i = 0; i <= HHOOK_SOCKET_LAST; i++) { + socket_hhook_register(i); + } } SYSINIT(socket, SI_SUB_PROTO_DOMAININIT, SI_ORDER_ANY, socket_init, NULL); @@ -333,6 +355,11 @@ soalloc(struct vnet *vnet) return (NULL); } #endif + if (khelp_init_osd(HELPER_CLASS_SOCKET, &so->osd)) { + uma_zfree(socket_zone, so); + return (NULL); + } + SOCKBUF_LOCK_INIT(&so->so_snd, "so_snd"); SOCKBUF_LOCK_INIT(&so->so_rcv, "so_rcv"); sx_init(&so->so_snd.sb_sx, "so_snd_sx"); @@ -348,6 +375,13 @@ soalloc(struct vnet *vnet) so->so_vnet = vnet; #endif mtx_unlock(&so_global_mtx); + + /* We shouldn't need the so_global_mtx */ + if (V_socket_hhh[HHOOK_SOCKET_CREATE]->hhh_nhooks > 0) { + if (hhook_run_socket(so, NULL, HHOOK_SOCKET_CREATE)) + /* Do we need more comprehensive error returns? */ + return (NULL); + } return (so); } @@ -384,7 +418,11 @@ sodealloc(struct socket *so) #ifdef MAC mac_socket_destroy(so); #endif + if (V_socket_hhh[HHOOK_SOCKET_CLOSE]->hhh_nhooks > 0) + hhook_run_socket(so, NULL, HHOOK_SOCKET_CLOSE); + crfree(so->so_cred); + khelp_destroy_osd(&so->osd); sx_destroy(&so->so_snd.sb_sx); sx_destroy(&so->so_rcv.sb_sx); SOCKBUF_LOCK_DESTROY(&so->so_snd); @@ -2327,6 +2365,25 @@ sorflush(struct socket *so) sbrelease_internal(&asb, so); } +/* + * Wrapper for Socket established helper hook. + * Parameters: socket, context of the hook point, hook id. + */ +static int inline +hhook_run_socket(struct socket *so, void *hctx, int32_t h_id) +{ + struct socket_hhook_data hhook_data = { + .so = so, + .hctx = hctx, + .m = NULL + }; + + hhook_run_hooks(V_socket_hhh[h_id], &hhook_data, &so->osd); + + /* Ugly but needed, since hhooks return void for now */ + return (hhook_data.status); +} + /* * Perhaps this routine, and sooptcopyout(), below, ought to come in an * additional variant to handle the case where the option value needs to be @@ -2572,7 +2629,11 @@ sosetopt(struct socket *so, struct sockopt *sopt) break; default: - error = ENOPROTOOPT; + if (V_socket_hhh[HHOOK_SOCKET_OPT]->hhh_nhooks > 0) + error = hhook_run_socket(so, sopt, + HHOOK_SOCKET_OPT); + else + error = ENOPROTOOPT; break; } if (error == 0 && so->so_proto->pr_ctloutput != NULL) @@ -2755,7 +2816,11 @@ integer: goto integer; default: - error = ENOPROTOOPT; + if (V_socket_hhh[HHOOK_SOCKET_OPT]->hhh_nhooks > 0) + error = hhook_run_socket(so, sopt, + HHOOK_SOCKET_OPT); + else + error = ENOPROTOOPT; break; } } @@ -3160,10 +3225,20 @@ filt_soread(struct knote *kn, long hint) return (1); } else if (so->so_error) /* temporary udp error */ return (1); - else if (kn->kn_sfflags & NOTE_LOWAT) - return (kn->kn_data >= kn->kn_sdata); - else - return (so->so_rcv.sb_cc >= so->so_rcv.sb_lowat); + + if (kn->kn_sfflags & NOTE_LOWAT) { + if (kn->kn_data >= kn->kn_sdata) + return 1; + } else { + if (so->so_rcv.sb_cc >= so->so_rcv.sb_lowat) + return 1; + } + + if (V_socket_hhh[HHOOK_FILT_SOREAD]->hhh_nhooks > 0) + /* This hook returning non-zero indicates an event, not error */ + return (hhook_run_socket(so, NULL, HHOOK_FILT_SOREAD)); + + return (0); } static void @@ -3187,6 +3262,10 @@ filt_sowrite(struct knote *kn, long hint) so = kn->kn_fp->f_data; SOCKBUF_LOCK_ASSERT(&so->so_snd); kn->kn_data = sbspace(&so->so_snd); + + if (V_socket_hhh[HHOOK_FILT_SOWRITE]->hhh_nhooks > 0) + hhook_run_socket(so, kn, HHOOK_FILT_SOWRITE); + if (so->so_snd.sb_state & SBS_CANTSENDMORE) { kn->kn_flags |= EV_EOF; kn->kn_fflags = so->so_error; diff --git a/sys/sys/hhook.h b/sys/sys/hhook.h index a54e980e111..9d5d8e33b2b 100644 --- a/sys/sys/hhook.h +++ b/sys/sys/hhook.h @@ -64,6 +64,7 @@ /* Helper hook types. */ #define HHOOK_TYPE_TCP 1 +#define HHOOK_TYPE_SOCKET 2 struct helper; struct osd; diff --git a/sys/sys/khelp.h b/sys/sys/khelp.h index db12d6bbfba..f542b1487d4 100644 --- a/sys/sys/khelp.h +++ b/sys/sys/khelp.h @@ -55,6 +55,7 @@ struct osd; /* Helper classes. */ #define HELPER_CLASS_TCP 0x00000001 +#define HELPER_CLASS_SOCKET 0x00000002 /* Public KPI functions. */ int khelp_register_helper(struct helper *h); diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index 90903406156..5c3933bfd94 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -38,6 +38,7 @@ #include /* for struct selinfo */ #include #include +#include #include #include #include @@ -117,6 +118,7 @@ struct socket { void *so_accept_filter_arg; /* saved filter args */ char *so_accept_filter_str; /* saved user args */ } *so_accf; + struct osd osd; /* Object Specific extensions */ /* * so_fibnum, so_user_cookie and friends can be used to attach * some user-specified metadata to a socket, which then can be @@ -292,6 +294,26 @@ MALLOC_DECLARE(M_PCB); MALLOC_DECLARE(M_SONAME); #endif +/* + * Socket specific helper hook point identifiers + * Do not leave holes in the sequence, hook registration is a loop. + */ +#define HHOOK_SOCKET_OPT 0 +#define HHOOK_SOCKET_CREATE 1 +#define HHOOK_SOCKET_RCV 2 +#define HHOOK_SOCKET_SND 3 +#define HHOOK_FILT_SOREAD 4 +#define HHOOK_FILT_SOWRITE 5 +#define HHOOK_SOCKET_CLOSE 6 +#define HHOOK_SOCKET_LAST HHOOK_SOCKET_CLOSE + +struct socket_hhook_data { + struct socket *so; + struct mbuf *m; + void *hctx; /* hook point specific data*/ + int status; +}; + extern int maxsockets; extern u_long sb_max; extern so_gen_t so_gencnt;