@@ -50,6 +50,8 @@
#include <netinet/tcp.h>
#include <unistd.h>
#include <semaphore.h>
+#include <ctype.h>
+#include <stdlib.h>
#include <rdma/rdma_cma.h>
#include <rdma/rdma_verbs.h>
@@ -122,6 +124,210 @@ struct fd_info {
atomic_t refcnt;
};
+typedef struct {
+ char *name;
+ uint32_t domain;
+ uint32_t type;
+ uint32_t protocol;
+} config_entry_t;
+
+static config_entry_t *entryp;
+static int16_t nentries;
+static int16_t config_avail;
+extern char *program_invocation_short_name;
+
+/* scan preload configuration file and create
+ * in-memory config store
+ * should be called only once under lock
+ */
+static int scan_preload_config(void)
+{
+ FILE *fp;
+ char line[512];
+ char *lp, *cp, *str1, *str2;
+ char *token, *subtoken, *saveptr1, *saveptr2;
+ int i, j, ret = 0;
+
+ fp = fopen(RS_CONF_DIR "/preload_config", "r");
+ if (fp == NULL) {
+ return -1;
+ }
+
+ while ((lp = fgets(line, sizeof(line), fp)) != NULL) {
+
+ /* trim white space at the beginning of each line */
+ while (*lp != '\0') {
+ if (isspace(*lp)) {
+ lp++;
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ /* skip comment and blank lines */
+ if (*lp == '\0' || *lp == '#') {
+ continue;
+ }
+
+ /* trim comments and newlines at the end of each line */
+ if ((cp = strpbrk(lp, "#\n")) != NULL) {
+ *cp = '\0';
+ }
+
+ /* now allocate memory for new configuration entry */
+ entryp = (config_entry_t *) realloc(entryp, (nentries + 1) *
sizeof(config_entry_t));
+ if (!entryp) {
+ ret = -1;
+ goto scan_done;
+ }
+ memset(entryp + nentries, '\0', sizeof(config_entry_t));
+
+ /* tokenize the retrieved line and parse individual fields */
+ for (i = 1, str1 = lp; ; i++, str1 = NULL) {
+ token = strtok_r(str1, " \t", &saveptr1);
+ if (token == NULL) {
+ break;
+ }
+
+ /* first field should contain program name */
+ if (i == 1) {
+ entryp[nentries].name = (char *) malloc((strlen(token) + 1));
+ if (!entryp[nentries].name) {
+ ret = -1;
+ goto scan_done;
+ }
+ memcpy(entryp[nentries].name, token, strlen(token) + 1);
+ continue;
+ }
+
+ /* second field onwards can contain multiple entries separate by
comma */
+ for (j = 1, str2 = token; ; j++, str2 = NULL) {
+ subtoken = strtok_r(str2, ",", &saveptr2);
+ if (subtoken == NULL) {
+ break;
+ }
+
+ /* second field is socket domain
+ * rsocket currently recognizes only AF_INET, AF_INET6 and AF_IB
domains
+ * '*' implies all the valid domains
+ */
+ if (i == 2) {
+ if (*subtoken == '*') {
+ entryp[nentries].domain |= (1 << AF_INET);
+ entryp[nentries].domain |= (1 << AF_INET6);
+ entryp[nentries].domain |= (1 << AF_IB);
+ break;
+ } else if (strcmp(subtoken, "inet6") == 0) {
+ entryp[nentries].domain |= (1 << AF_INET6);
+ } else if (strcmp(subtoken, "inet") == 0) {
+ entryp[nentries].domain |= (1 << AF_INET);
+ } else if (strcmp(subtoken, "ib") == 0) {
+ entryp[nentries].domain |= (1 << AF_IB);
+ }
+ continue;
+ }
+
+ /* third field is socket type
+ * rsocket currently recognizes only SOCK_STREAM and SOCK_DGRAM
types
+ * '*' implies all the valid types
+ */
+ if (i == 3) {
+ if (*subtoken == '*') {
+ entryp[nentries].type |= (1 << SOCK_STREAM);
+ entryp[nentries].type |= (1 << SOCK_DGRAM);
+ break;
+ } else if (strcmp(subtoken, "stream") == 0) {
+ entryp[nentries].type |= (1 << SOCK_STREAM);
+ } else if (strcmp(subtoken, "dgram") == 0) {
+ entryp[nentries].type |= (1 << SOCK_DGRAM);
+ }
+ continue;
+ }
+
+ /* fourth field is socket protocol
+ * rsocket currently recgonizes only IPPROTO_TCP and IPPROTO_UDP
protocols
+ * '*' implies all the valid protocols
+ */
+ if (i == 4) {
+ if (*subtoken == '*') {
+ entryp[nentries].protocol |= (1 << IPPROTO_TCP);
+ entryp[nentries].protocol |= (1 << IPPROTO_UDP);
+ break;
+ } else if (strcmp(subtoken, "tcp") == 0) {
+ entryp[nentries].protocol |= (1 << IPPROTO_TCP);
+ } else if (strcmp(subtoken, "udp") == 0) {
+ entryp[nentries].protocol |= (1 << IPPROTO_UDP);
+ }
+ continue;
+ }
+ }
+ }
+ nentries += 1;
+ }
+
+scan_done:
+ fclose(fp);
+ return ret;
+}
+
+/* free in-memory config store
+ * should be called only once during finalization
+ */
+static void free_preload_config(void)
+{
+ int i;
+
+ if (entryp) {
+ for (i = 0; i < nentries; i++) {
+ if (entryp[i].name) {
+ free(entryp[i].name);
+ }
+ }
+ free(entryp);
+ }
+
+ return;
+}
+
+/* check whether interception is required for this socket
+ * compares the provided attributes with that available in the
in-memory
+ * data store for the current process
+ * sets-up in-memory config store if it's already not done
+ */
+static int intercept_socket(int domain, int type, int protocol)
+{
+ int i;
+
+ /* locate the config entry */
+ for (i = 0; i < nentries; i++) {
+ if (strncmp(entryp[i].name, program_invocation_short_name,
strlen(entryp[i].name)) == 0) {
+ break;
+ }
+ }
+ if (i == nentries) {
+ return 0;
+ }
+
+ /* match domain field */
+ if (!(entryp[i].domain & (1 << domain))) {
+ return 0;
+ }
+
+ /* match type field */
+ if (!(entryp[i].type & (1 << type))) {
+ return 0;
+ }
+
+ /* match protocol field only if protocol is specified */
+ if (protocol && !(entryp[i].protocol & (1 << protocol))) {
+ return 0;
+ }
+