From 35503f4765b81a55a8ba11617f263e5f0d385c1b Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Fri, 4 Feb 2022 07:59:08 -0800 Subject: [PATCH] TMXR: Add Access Control List (ACL) support for incoming network connections --- doc/simh_doc.doc | Bin 290304 -> 294912 bytes sim_console.c | 12 +- sim_tmxr.c | 289 ++++++++++++++++++++++++++++++++++------------- sim_tmxr.h | 6 + 4 files changed, 226 insertions(+), 81 deletions(-) diff --git a/doc/simh_doc.doc b/doc/simh_doc.doc index 8edf34030b0ff582feeaa487ad3fa5252cb942f2..d1c110dcc5076b4cecfd72a9a790b8e37031b1f3 100644 GIT binary patch delta 45173 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 delta 41800 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 Mn1A7ku=1t;AGh>U7XSbN diff --git a/sim_console.c b/sim_console.c index 13a26b05..d9b1ca6e 100644 --- a/sim_console.c +++ b/sim_console.c @@ -1898,8 +1898,16 @@ static t_stat sim_set_rem_telnet (int32 flag, CONST char *cptr) t_stat r; if (flag) { - r = sim_parse_addr (cptr, NULL, 0, NULL, NULL, 0, NULL, NULL); + char gbuf[CBUFSIZE]; + char *cp; + + strlcpy (gbuf, cptr, sizeof (gbuf)); + if ((cp = strchr (gbuf, ';'))) + *cp = '\0'; + r = sim_parse_addr (gbuf, NULL, 0, NULL, NULL, 0, NULL, NULL); if (r == SCPE_OK) { + if (cp != NULL) + *cp = ';'; if (sim_rem_con_tmxr.master) /* already open? */ sim_set_rem_telnet (0, NULL); /* close first */ if (sim_rem_con_tmxr.lines == 0) /* if no connection limit set */ @@ -2473,6 +2481,8 @@ while (*cptr != 0) { /* do all mods */ return r; } else { + if (cvptr) /* if we removed a = sign */ + *(--cvptr) = '='; /* restore it */ if (sim_con_tmxr.master) /* already open? */ sim_set_notelnet (0, NULL); /* close first */ r = tmxr_attach (&sim_con_tmxr, &sim_con_unit, gbuf);/* open master socket */ diff --git a/sim_tmxr.c b/sim_tmxr.c index 23d5f0f4..782a0d2b 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -916,11 +916,21 @@ tptr = (char *) calloc (1, 1); if (tptr == NULL) /* no more mem? */ return tptr; -if (mp->port) /* copy port */ +if (mp->port) { /* copy port */ sprintf (growstring(&tptr, 33 + strlen (mp->port)), "%s%s", mp->port, mp->notelnet ? ";notelnet" : (mp->nomessage ? ";nomessage" : "")); + if (mp->acl) { /* copy acl in pieces */ + char gbuf[CBUFSIZE]; + const char *c = mp->acl; + + while (*c != '\0') { + c = get_glyph_nc (c, gbuf, ','); + sprintf (growstring(&tptr, 9 + strlen (gbuf)), ";%s=%s", (gbuf[0] == '+') ? "Accept" : "Reject", gbuf + 1); + } + } + } if (mp->logfiletmpl[0]) /* logfile info */ sprintf (growstring(&tptr, 7 + strlen (mp->logfiletmpl)), ",Log=%s", mp->logfiletmpl); if (mp->buffered) @@ -989,10 +999,20 @@ if (lp->destination || lp->port || lp->txlogname || (lp->conn == TMXR_LINE_DISAB sprintf (growstring(&tptr, 8), ",%s", lp->datagram ? "UDP" : "TCP"); if (lp->mp->packet != lp->packet) sprintf (growstring(&tptr, 8), ",Packet"); - if (lp->port) + if (lp->port) { sprintf (growstring(&tptr, 32 + strlen (lp->port)), ",%s%s%s", lp->port, ((lp->mp->notelnet != lp->notelnet) && (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : "", ((lp->mp->nomessage != lp->nomessage) && (!lp->datagram)) ? (lp->nomessage ? ";nomessage" : ";message") : ""); + if (lp->acl) { /* copy acl in pieces */ + char gbuf[CBUFSIZE]; + const char *c = lp->acl; + + while (*c != '\0') { + c = get_glyph_nc (c, gbuf, ','); + sprintf (growstring(&tptr, 9 + strlen (gbuf)), ";%s=%s", (gbuf[0] == '+') ? "Accept" : "Reject", gbuf + 1); + } + } + } if (lp->destination) { if (lp->serport) { char portname[CBUFSIZE]; @@ -1120,94 +1140,106 @@ if (mp->master) { i = mp->lines; /* play it safe in case lines == 0 */ ++mp->sessions; /* count the new session */ - for (j = 0; j < mp->lines; j++, i++) { /* find next avail line */ - if (op && (*op >= 0) && (*op < mp->lines)) /* order list present and valid? */ - i = *op++; /* get next line in list to try */ - else /* no list or not used or range error */ - i = j; /* get next sequential line */ - - lp = mp->ldsc + i; /* get pointer to line descriptor */ - if ((lp->conn == FALSE) && /* is the line available? */ - (lp->destination == NULL) && - (lp->master == 0) && - (lp->ser_connect_pending == FALSE) && - (lp->modem_control ? ((lp->modembits & TMXR_MDM_DTR) != 0) : TRUE)) - break; /* yes, so stop search */ + if (mp->acl) { + if (sim_addr_acl_check (address, mp->acl) != 0) { + tmxr_debug_connect (mp, "tmxr_poll_conn() - Connection Specifically rejected by ACL"); + sim_close_sock (newsock); + free (address); + ++mp->acl_rejected_sessions; + } + else + ++mp->acl_accepted_sessions; } + else { + for (j = 0; j < mp->lines; j++, i++) { /* find next avail line */ + if (op && (*op >= 0) && (*op < mp->lines)) /* order list present and valid? */ + i = *op++; /* get next line in list to try */ + else /* no list or not used or range error */ + i = j; /* get next sequential line */ - if (i >= mp->lines) { /* all busy? */ - int32 ringable_count = 0; - - for (j = 0; j < mp->lines; j++, i++) { /* find next avail line */ - lp = mp->ldsc + j; /* get pointer to line descriptor */ - if (lp->framer) - continue; - - if ((lp->conn == FALSE) && /* is the line available? */ + lp = mp->ldsc + i; /* get pointer to line descriptor */ + if ((lp->conn == FALSE) && /* is the line available? */ (lp->destination == NULL) && (lp->master == 0) && (lp->ser_connect_pending == FALSE) && - ((lp->modembits & TMXR_MDM_DTR) == 0)) { - ++ringable_count; - lp->modembits |= TMXR_MDM_RNG; - tmxr_debug_connect_line (lp, "tmxr_poll_conn() - Ringing line"); - } + (lp->modem_control ? ((lp->modembits & TMXR_MDM_DTR) != 0) : TRUE)) + break; /* yes, so stop search */ } - if (ringable_count > 0) { - ringing = -2; - if (mp->ring_start_time == 0) { - mp->ring_start_time = poll_time; - mp->ring_sock = newsock; - mp->ring_ipad = address; + + if (i >= mp->lines) { /* all busy? */ + int32 ringable_count = 0; + + for (j = 0; j < mp->lines; j++, i++) { /* find next avail line */ + lp = mp->ldsc + j; /* get pointer to line descriptor */ + if (lp->framer) + continue; + + if ((lp->conn == FALSE) && /* is the line available? */ + (lp->destination == NULL) && + (lp->master == 0) && + (lp->ser_connect_pending == FALSE) && + ((lp->modembits & TMXR_MDM_DTR) == 0)) { + ++ringable_count; + lp->modembits |= TMXR_MDM_RNG; + tmxr_debug_connect_line (lp, "tmxr_poll_conn() - Ringing line"); + } } - else { - if ((poll_time - mp->ring_start_time) < TMXR_MODEM_RING_TIME*1000) { + if (ringable_count > 0) { + ringing = -2; + if (mp->ring_start_time == 0) { + mp->ring_start_time = poll_time; mp->ring_sock = newsock; mp->ring_ipad = address; } - else { /* Timeout waiting for DTR */ - int ln; - - /* turn off pending ring signals */ - for (ln = 0; ln < lp->mp->lines; ln++) { - TMLN *tlp = lp->mp->ldsc + ln; - if (((tlp->destination == NULL) && (tlp->master == 0)) && - (tlp->modembits & TMXR_MDM_RNG) && (tlp->conn == FALSE)) - tlp->modembits &= ~TMXR_MDM_RNG; + else { + if ((poll_time - mp->ring_start_time) < TMXR_MODEM_RING_TIME*1000) { + mp->ring_sock = newsock; + mp->ring_ipad = address; + } + else { /* Timeout waiting for DTR */ + int ln; + + /* turn off pending ring signals */ + for (ln = 0; ln < lp->mp->lines; ln++) { + TMLN *tlp = lp->mp->ldsc + ln; + if (((tlp->destination == NULL) && (tlp->master == 0)) && + (tlp->modembits & TMXR_MDM_RNG) && (tlp->conn == FALSE)) + tlp->modembits &= ~TMXR_MDM_RNG; + } + mp->ring_start_time = 0; + tmxr_msg (newsock, "No answer on any connection\r\n"); + tmxr_debug_connect (mp, "tmxr_poll_conn() - No Answer - All connections busy"); + sim_close_sock (newsock); + free (address); } - mp->ring_start_time = 0; - tmxr_msg (newsock, "No answer on any connection\r\n"); - tmxr_debug_connect (mp, "tmxr_poll_conn() - No Answer - All connections busy"); - sim_close_sock (newsock); - free (address); } } + else { + tmxr_msg (newsock, "All connections busy\r\n"); + tmxr_debug_connect (mp, "tmxr_poll_conn() - All connections busy"); + sim_close_sock (newsock); + free (address); + } } else { - tmxr_msg (newsock, "All connections busy\r\n"); - tmxr_debug_connect (mp, "tmxr_poll_conn() - All connections busy"); - sim_close_sock (newsock); - free (address); + lp = mp->ldsc + i; /* get line desc */ + lp->conn = TRUE; /* record connection */ + lp->sock = newsock; /* save socket */ + lp->ipad = address; /* ip address */ + tmxr_init_line (lp); /* init line */ + lp->notelnet = mp->notelnet; /* apply mux default telnet setting */ + lp->nomessage = mp->nomessage; /* apply mux default telnet setting */ + if (!lp->notelnet) { + sim_write_sock (newsock, (char *)mantra, sizeof(mantra)); + tmxr_debug (TMXR_DBG_XMT, lp, "Sending", (char *)mantra, sizeof(mantra)); + lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256); + memset (lp->telnet_sent_opts, 0, 256); + } + tmxr_report_connection (mp, lp); + lp->cnms = sim_os_msec (); /* time of connection */ + return i; } } - else { - lp = mp->ldsc + i; /* get line desc */ - lp->conn = TRUE; /* record connection */ - lp->sock = newsock; /* save socket */ - lp->ipad = address; /* ip address */ - tmxr_init_line (lp); /* init line */ - lp->notelnet = mp->notelnet; /* apply mux default telnet setting */ - lp->nomessage = mp->nomessage; /* apply mux default telnet setting */ - if (!lp->notelnet) { - sim_write_sock (newsock, (char *)mantra, sizeof(mantra)); - tmxr_debug (TMXR_DBG_XMT, lp, "Sending", (char *)mantra, sizeof(mantra)); - lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256); - memset (lp->telnet_sent_opts, 0, 256); - } - tmxr_report_connection (mp, lp); - lp->cnms = sim_os_msec (); /* time of connection */ - return i; - } } /* end if newsock */ } @@ -1296,6 +1328,18 @@ for (i = 0; i < mp->lines; i++) { /* check each line in se free (peername); ++mp->sessions; /* count the new session */ + if (lp->acl) { /* Restrict connection with ACL rules? */ + if (sim_addr_acl_check (address, lp->acl) != 0) { + snprintf (msg, sizeof (msg) -1, "tmxr_poll_conn() - ACL Rejecting line connection from: %s", address); + tmxr_debug_connect_line (lp, msg); + sim_close_sock (newsock); + free (address); + ++lp->acl_rejected_sessions; + continue; /* Go back for another connection */ + } + else + ++lp->acl_accepted_sessions; + } if (lp->destination) { /* Virtual Null Modem Cable? */ char host[sizeof(msg) - 64]; @@ -2858,7 +2902,8 @@ t_stat tmxr_open_master (TMXR *mp, CONST char *cptr) int32 i, line, nextline = -1; char tbuf[CBUFSIZE], listen[CBUFSIZE], destination[CBUFSIZE], logfiletmpl[CBUFSIZE], buffered[CBUFSIZE], hostport[CBUFSIZE], - port[CBUFSIZE], option[CBUFSIZE], speed[CBUFSIZE], dev_name[CBUFSIZE]; + port[CBUFSIZE], option[CBUFSIZE], speed[CBUFSIZE], dev_name[CBUFSIZE], + acl[CBUFSIZE]; char framer[CBUFSIZE],fr_eth[CBUFSIZE]; int num; int8 fr_mode; @@ -2896,6 +2941,7 @@ while (*tptr) { memset(destination, '\0', sizeof(destination)); memset(buffered, '\0', sizeof(buffered)); memset(port, '\0', sizeof(port)); + memset(acl, '\0', sizeof(acl)); memset(option, '\0', sizeof(option)); memset(speed, '\0', sizeof(speed)); memset(framer, '\0', sizeof(framer)); @@ -3026,6 +3072,7 @@ while (*tptr) { cptr = get_glyph (gbuf, port, ';'); if (sim_parse_addr (port, NULL, 0, NULL, NULL, 0, NULL, NULL)) return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port); + memset (acl, '\0', sizeof (acl)); while (cptr && *cptr) { char *tptr = gbuf + (cptr - gbuf); @@ -3041,10 +3088,28 @@ while (*tptr) { else if (0 == MATCH_CMD (tptr, "MESSAGE")) listennomessage = FALSE; - else { - if (*tptr) - return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", tptr); - } + else + if (0 == memcmp (option, "ACCEPT=", 7)) { + if (sim_addr_acl_check (option + 7, NULL)) + return sim_messagef (SCPE_ARG, "Invalid Accept Criteria: %s\n", option + 7); + if (acl[0] != '\0') + strlcat (acl, ",", sizeof (acl)); + strlcat (acl, "+", sizeof (acl)); /* Tag as Accept rule */ + strlcat (acl, option + 7, sizeof (acl)); + } + else + if (0 == memcmp (option, "REJECT=", 7)) { + if (sim_addr_acl_check (option + 7, NULL)) + return sim_messagef (SCPE_ARG, "Invalid Reject Criteria: %s\n", option + 7); + if (acl[0] != '\0') + strlcat (acl, ",", sizeof (acl)); + strlcat (acl, "-", sizeof (acl)); /* Tag as Reject rule */ + strlcat (acl, option + 7, sizeof (acl)); + } + else { + if (*tptr) + return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", tptr); + } } cptr = init_cptr; } @@ -3057,6 +3122,7 @@ while (*tptr) { sim_close_sock (sock); sim_os_ms_sleep (2); /* let the close finish (required on some platforms) */ strcpy (listen, port); + memset (acl, '\0', sizeof (acl)); cptr = get_glyph (cptr, option, ';'); while (option[0]) { if (0 == MATCH_CMD (option, "NOTELNET")) @@ -3071,7 +3137,25 @@ while (*tptr) { if (0 == MATCH_CMD (option, "MESSAGE")) listennomessage = FALSE; else - return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", option); + if (0 == memcmp (option, "ACCEPT=", 7)) { + if (sim_addr_acl_check (option + 7, NULL)) + return sim_messagef (SCPE_ARG, "Invalid Accept Criteria: %s\n", option + 7); + if (acl[0] != '\0') + strlcat (acl, ",", sizeof (acl)); + strlcat (acl, "+", sizeof (acl)); /* Tag as Accept rule */ + strlcat (acl, option + 7, sizeof (acl)); + } + else + if (0 == memcmp (option, "REJECT=", 7)) { + if (sim_addr_acl_check (option + 7, NULL)) + return sim_messagef (SCPE_ARG, "Invalid Reject Criteria: %s\n", option + 7); + if (acl[0] != '\0') + strlcat (acl, ",", sizeof (acl)); + strlcat (acl, "-", sizeof (acl)); /* Tag as Reject rule */ + strlcat (acl, option + 7, sizeof (acl)); + } + else + return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", option); cptr = get_glyph (cptr, option, ';'); } } @@ -3249,6 +3333,8 @@ while (*tptr) { mp->ring_start_time = 0; mp->notelnet = listennotelnet; /* save desired telnet behavior flag */ mp->nomessage = listennomessage; /* save desired telnet behavior flag */ + if (acl[0]) + mp->acl = strdup (acl); /* save specified access control list */ for (i = 0; i < mp->lines; i++) { /* initialize lines */ lp = mp->ldsc + i; lp->mp = mp; /* set the back pointer */ @@ -3463,6 +3549,8 @@ while (*tptr) { lp->nomessage = listennomessage; else lp->nomessage = mp->nomessage; + if (acl[0]) + lp->acl = strdup (acl); } if (destination[0]) { serport = sim_open_serial (destination, lp, &r); @@ -4455,6 +4543,10 @@ if (attach) free (attach); tmxr_show_summ(st, NULL, 0, mp); fprintf(st, ", sessions=%d", mp->sessions); +if (mp->acl_accepted_sessions) + fprintf(st, ", accepted=%d", mp->acl_accepted_sessions); +if (mp->acl_rejected_sessions) + fprintf(st, ", rejected=%d", mp->acl_rejected_sessions); if (mp->lines == 1) { if (mp->ldsc->rxbps) { fprintf(st, ", Speed=%d", mp->ldsc->rxbps); @@ -4497,6 +4589,13 @@ for (j = 0; j < mp->lines; j++) { if (lp->bpsfactor != 1.0) fprintf(st, ", Speed=*%.0f bps", lp->bpsfactor); } + if (lp->sessions) { + fprintf(st, ", Sessions=%d", lp->sessions); + if (lp->acl_accepted_sessions) + fprintf(st, ", Accepted=%d", lp->acl_accepted_sessions); + if (lp->acl_rejected_sessions) + fprintf(st, ", Rejected=%d", lp->acl_rejected_sessions); + } fprintf (st, "\n"); } if ((!lp->sock) && (!lp->connecting) && (!lp->serport) && (!lp->master)) { @@ -4584,6 +4683,8 @@ for (i = 0; i < mp->lines; i++) { /* loop thru conn */ } free (lp->destination); lp->destination = NULL; + free (lp->acl); + lp->acl = NULL; if (lp->connecting) { lp->sock = lp->connecting; lp->connecting = 0; @@ -4845,6 +4946,15 @@ if (single_line) { /* Single Line Multiplexer */ } fprintf (st, "A Telnet listening port can be configured with:\n\n"); fprintf (st, " sim> ATTACH %s {interface:}port\n\n", dptr->name); + fprintf (st, "Connections to the specified port, by default, will be unrestricted.\n"); + fprintf (st, "Connections from particular IPv4 or IPv6 addresses can be restricted\n"); + fprintf (st, "or allowed based on rules you can add to the \"{interface:}port\"\n"); + fprintf (st, "specifier on the attach command. You can add as many rules as you need\n"); + fprintf (st, "to the attach command specified with \";ACCEPT=rule-detail\" or\n"); + fprintf (st, "\";REJECT=rule-detail\" where rule-detail can be an IP address, hostname\n"); + fprintf (st, "or network block in CIDR form. Rules are interpreted in order and if,\n"); + fprintf (st, "while processing the list, the end is reached the connection will be\n"); + fprintf (st, "rejected.\n\n"); fprintf (st, "The -U switch can be specified on the attach command that specifies\n"); fprintf (st, "a listening port. This will allow a listening port to be reused if\n"); fprintf (st, "some prior connections haven't completely shutdown.\n\n"); @@ -5012,6 +5122,15 @@ else { fprintf (st, "Line specific tcp listening ports are supported. These are configured\n"); fprintf (st, "using commands of the form:\n\n"); fprintf (st, " sim> ATTACH %s Line=n,{interface:}port{;notelnet}|{;nomessage}\n\n", dptr->name); + fprintf (st, "Connections to the specified port, by default, will be unrestricted.\n"); + fprintf (st, "Connections from particular IPv4 or IPv6 addresses can be restricted\n"); + fprintf (st, "or allowed based on rules you can add to the \"{interface:}port\"\n"); + fprintf (st, "specifier on the attach command. You can add as many rules as you need\n"); + fprintf (st, "to the attach command specified with \";ACCEPT=rule-detail\" or\n"); + fprintf (st, "\";REJECT=rule-detail\" where rule-detail can be an IP address, hostname\n"); + fprintf (st, "or network block in CIDR form. Rules are interpreted in order and if,\n"); + fprintf (st, "while processing the list, the end is reached the connection will be\n"); + fprintf (st, "rejected.\n\n"); } fprintf (st, "Direct computer to computer connections (Virutal Null Modem cables) may\n"); fprintf (st, "be established using the telnet protocol or via raw tcp sockets.\n\n"); @@ -5194,8 +5313,11 @@ if (lp->sock) { free (peername); } -if ((lp->port) && (!lp->datagram)) +if ((lp->port) && (!lp->datagram)) { fprintf (st, "Listening on port %s\n", lp->port); /* print port name */ + if (lp->acl) + fprintf (st, "Connections will be accepted/rejected based on: %s\n", lp->acl); + } if (lp->serport) /* serial connection? */ fprintf (st, "Connected to serial port %s\n", lp->destination); /* print port name */ @@ -6036,6 +6158,13 @@ SIM_TEST(sim_parse_addr ("", NULL, 0, "localhost", NULL, 0, "1234", NULL) != -1) SIM_TEST(sim_parse_addr ("", host, 0, "localhost", NULL, 0, "1234", NULL) != -1); SIM_TEST(sim_parse_addr ("", host, sizeof(host), "localhost", port, 0, "1234", NULL) != -1); SIM_TEST((sim_parse_addr ("", host, sizeof(host), "localhost", port, sizeof(port), "1234", NULL) == -1) || (strcmp(host, "localhost")) || (strcmp(port,"1234"))); +SIM_TEST(sim_addr_acl_check ("127.0.0.1", NULL) == -1); +SIM_TEST(sim_addr_acl_check ("127.0.0.1/0", NULL) != -1); +SIM_TEST(sim_addr_acl_check ("127.0.0.1/32", NULL) == -1); +SIM_TEST(sim_addr_acl_check ("127.0.0.1/64", NULL) != -1); +SIM_TEST(sim_addr_acl_check ("127.0.0.6", "+127.0.0.1/32,-127.0.0.2") != -1); +SIM_TEST(sim_addr_acl_check ("127.0.0.2", "+127.0.0.1,-127.0.0.2/32,+127.0.0.3") != -1); +SIM_TEST(sim_parse_addr ("", host, sizeof(host), "localhost", port, sizeof(port), "1234", "127.0.0.1") == -1); SIM_TEST((sim_parse_addr ("localhost:6666", host, sizeof(host), "localhost", port, sizeof(port), "1234", NULL) == -1) || (strcmp(host, "localhost")) || (strcmp(port,"6666"))); SIM_TEST(sim_parse_addr ("localhost:66666", host, sizeof(host), "localhost", port, sizeof(port), "1234", NULL) != -1); SIM_TEST((sim_parse_addr ("localhost:telnet", host, sizeof(host), "localhost", port, sizeof(port), "1234", NULL) == -1) || (strcmp(host, "localhost")) || (strcmp(port,"telnet"))); diff --git a/sim_tmxr.h b/sim_tmxr.h index 35c8c08a..feb0f61a 100644 --- a/sim_tmxr.h +++ b/sim_tmxr.h @@ -141,6 +141,9 @@ struct tmln { char *ipad; /* IP address */ SOCKET master; /* line specific master socket */ char *port; /* line specific listening port */ + char *acl; /* Access control list (CIDR) to accept or reject connects from */ + int32 acl_accepted_sessions; /* count of ACL accepted tcp connections */ + int32 acl_rejected_sessions; /* count of ACL rejected tcp connections */ int32 sessions; /* count of tcp connections received */ uint32 cnms; /* conn time */ int32 tsta; /* Telnet state */ @@ -218,6 +221,9 @@ struct tmxr { TMLN *ldsc; /* line descriptors */ int32 *lnorder; /* line connection order */ DEVICE *dptr; /* multiplexer device */ + char *acl; /* Access control list (CIDR) to accept or reject connects from */ + int32 acl_accepted_sessions; /* count of ACL accepted tcp connections */ + int32 acl_rejected_sessions; /* count of ACL rejected tcp connections */ UNIT *uptr; /* polling unit (connection) */ char logfiletmpl[FILENAME_MAX]; /* template logfile name */ int32 txcount; /* count of transmit bytes */