summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/addr.c49
-rw-r--r--src/addr.h13
-rw-r--r--src/blob.c22
-rw-r--r--src/blob.h3
-rw-r--r--src/squark-auth-snmp.c30
5 files changed, 101 insertions, 16 deletions
diff --git a/src/addr.c b/src/addr.c
index 47013f2..f79a26d 100644
--- a/src/addr.c
+++ b/src/addr.c
@@ -13,14 +13,22 @@ int addr_len(const sockaddr_any *addr)
}
}
-sockaddr_any *addr_parse(blob_t b, sockaddr_any *addr)
+sockaddr_any *addr_parse_prefix(blob_t b, sockaddr_any *addr, uint8_t *prefix_len)
{
memset(addr, 0, sizeof(*addr));
- addr->ipv4.sin_family = AF_INET;
- addr->ipv4.sin_addr.s_addr = blob_inet_addr(b);
- if (addr->ipv4.sin_addr.s_addr == -1)
- return NULL;
- return addr;
+
+ if (blob_pull_inet_addr(&b, &addr->ipv4.sin_addr)) {
+ addr->ipv4.sin_family = AF_INET;
+ if (blob_pull_matching(&b, BLOB_STR("/"))) {
+ if (prefix_len == NULL)
+ return NULL;
+ *prefix_len = blob_pull_uint(&b, 10);
+ }
+ if (b.len != 0)
+ return NULL;
+ return addr;
+ }
+ return NULL;
}
unsigned long addr_hash(const sockaddr_any *addr)
@@ -72,3 +80,32 @@ void addr_push_hostaddr(blob_t *b, const sockaddr_any *addr)
}
blob_push(b, f);
}
+
+static int bitcmp(const uint8_t *a, const uint8_t *b, int len)
+{
+ int bytes, bits, mask, r;
+
+ bytes = len / 8;
+ bits = len % 8;
+
+ if (bytes != 0) {
+ r = memcmp(a, b, bytes);
+ if (r != 0)
+ return r;
+ }
+ if (bits != 0) {
+ mask = (0xff << (8 - bits)) & 0xff;
+ return ((int) (a[bytes] & mask)) - ((int) (b[bytes] & mask));
+ }
+ return 0;
+}
+
+int addr_prefix_cmp(const sockaddr_any *a, const sockaddr_any *b, int prefix)
+{
+ if (a->any.sa_family != b->any.sa_family)
+ return a->any.sa_family - b->any.sa_family;
+
+ return bitcmp((const uint8_t *) &a->ipv4.sin_addr.s_addr,
+ (const uint8_t *) &b->ipv4.sin_addr.s_addr,
+ prefix);
+}
diff --git a/src/addr.h b/src/addr.h
index 452d14b..945e03b 100644
--- a/src/addr.h
+++ b/src/addr.h
@@ -10,11 +10,22 @@ typedef union {
} sockaddr_any;
int addr_len(const sockaddr_any *addr);
-sockaddr_any *addr_parse(blob_t text, sockaddr_any *addr);
+sockaddr_any *addr_parse_prefix(blob_t text, sockaddr_any *addr, uint8_t *prefix);
unsigned long addr_hash(const sockaddr_any *addr);
const char *addr_print(const sockaddr_any *addr);
blob_t addr_get_hostaddr_blob(const sockaddr_any *addr);
void addr_push_hostaddr(blob_t *b, const sockaddr_any *addr);
+int addr_prefix_cmp(const sockaddr_any *a, const sockaddr_any *b, int prefix);
+
+static inline void addr_invalidate(sockaddr_any *addr)
+{
+ addr->any.sa_family = AF_UNSPEC;
+}
+
+static inline sockaddr_any *addr_parse(blob_t b, sockaddr_any *addr)
+{
+ return addr_parse_prefix(b, addr, NULL);
+}
static inline void addr_copy(sockaddr_any *dst, const sockaddr_any *src)
{
diff --git a/src/blob.c b/src/blob.c
index ea02ea4..a6ccc62 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -139,23 +139,31 @@ int blob_icmp(blob_t a, blob_t b)
return strncasecmp(a.ptr, b.ptr, a.len);
}
-unsigned long blob_inet_addr(blob_t b)
+int blob_pull_inet_addr(blob_t *b, struct in_addr *saddr)
{
unsigned long ip = 0;
int i;
for (i = 0; i < 3; i++) {
- ip += blob_pull_uint(&b, 10);
+ ip += blob_pull_uint(b, 10);
ip <<= 8;
- if (!blob_pull_matching(&b, BLOB_STR(".")))
+ if (!blob_pull_matching(b, BLOB_STR(".")))
return 0;
}
- ip += blob_pull_uint(&b, 10);
- if (b.len != 0)
- return 0;
- return htonl(ip);
+ ip += blob_pull_uint(b, 10);
+ saddr->s_addr = htonl(ip);
+ return 1;
}
+unsigned long blob_inet_addr(blob_t b)
+{
+ struct in_addr saddr;
+
+ if (blob_pull_inet_addr(&b, &saddr) && b.len == 0)
+ return saddr.s_addr;
+
+ return 0;
+}
blob_t blob_pushed(blob_t buffer, blob_t left)
{
diff --git a/src/blob.h b/src/blob.h
index 1fcaec0..971ce61 100644
--- a/src/blob.h
+++ b/src/blob.h
@@ -3,6 +3,8 @@
#include <string.h>
+struct in_addr;
+
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#if defined __GNUC__ && __GNUC__ == 2 && __GNUC_MINOR__ < 96
@@ -56,6 +58,7 @@ int blob_pull_matching(blob_t *b, blob_t e);
unsigned int blob_pull_uint(blob_t *b, int radix);
blob_t blob_pull_spn(blob_t *b, const blob_t spn);
blob_t blob_pull_cspn(blob_t *b, const blob_t cspn);
+int blob_pull_inet_addr(blob_t *b, struct in_addr *saddr);
blob_t blob_expand_head(blob_t *b, blob_t limits, unsigned char sep);
blob_t blob_expand_tail(blob_t *b, blob_t limits, unsigned char sep);
diff --git a/src/squark-auth-snmp.c b/src/squark-auth-snmp.c
index 90a908b..2ac1a7f 100644
--- a/src/squark-auth-snmp.c
+++ b/src/squark-auth-snmp.c
@@ -125,6 +125,9 @@ static int username_format_flags;
static const blob_t space = BLOB_STR_INIT(" ");
static const blob_t lf = BLOB_STR_INIT("\n");
+static sockaddr_any management_subnet;
+static uint8_t management_prefix = 0;
+
/* ----------------------------------------------------------------- */
#define BLOB_OID(objid) BLOB_BUF(objid)
@@ -366,7 +369,7 @@ struct switch_info *get_switch(sockaddr_any *addr)
config.community = (unsigned char *) snmp_community;
config.community_len = strlen(snmp_community);
}
- config.peername = (char *) addr_print(addr);
+ config.peername = strdup((char *) addr_print(addr));
si->session = snmp_open(&config);
si->next = all_switches[bucket];
@@ -717,6 +720,7 @@ static int auth_handle_portinfo_reply(int oper, netsnmp_session *s, int reqid, n
snprintf(auth->status_msg, sizeof(auth->status_msg)-1,
"required info missing: info_available=%08x",
auth->info_available);
+ dbg_printf("%s\n", auth->status_msg);
done:
if (kick_out && auth_ok(auth))
@@ -764,6 +768,8 @@ static void auth_query_port_info(struct auth_context *auth)
snprintf(auth->status_msg, sizeof(auth->status_msg)-1,
"%s: query port info (%d)",
si->session->peername, auth->local_port);
+ dbg_printf("%s\n", auth->status_msg);
+
auth_talk_snmp(auth, si->session, pdu, auth_handle_portinfo_reply);
}
@@ -793,6 +799,16 @@ static int auth_handle_lldp_reply(int oper, netsnmp_session *s, int reqid, netsn
/* We have mathing LLDP neighbor */
blob_pull_oid(&res);
blob_pull_iana_afn(&res, &spi->link_partner);
+
+ dbg_printf("%s: lldp neighbour is %s\n",
+ s->peername, addr_print(&spi->link_partner));
+
+ if (management_prefix != 0 &&
+ addr_prefix_cmp(&spi->link_partner, &management_subnet, management_prefix) != 0) {
+ dbg_printf("%s: not matching %s/%d\n",
+ s->peername, addr_print(&management_subnet), management_prefix);
+ addr_invalidate(&spi->link_partner);
+ }
cache_update(&spi->cache_control);
return 1;
}
@@ -1013,6 +1029,7 @@ static void auth_query_switch_info(struct auth_context *auth)
snprintf(auth->status_msg, sizeof(auth->status_msg)-1,
"%s: refresh switch information",
si->session->peername);
+ dbg_printf("%s\n", auth->status_msg);
if (!cache_refresh(&si->cache_control, auth, auth_query_fib))
return;
@@ -1020,6 +1037,7 @@ static void auth_query_switch_info(struct auth_context *auth)
snprintf(auth->status_msg, sizeof(auth->status_msg)-1,
"%s: query switch information",
si->session->peername);
+ dbg_printf("%s\n", auth->status_msg);
pdu = snmp_pdu_create(SNMP_MSG_GET);
snmp_add_null_var(pdu, oid_const(SNMPv2_MIB_sysName));
@@ -1090,6 +1108,8 @@ void start_authentication(blob_t token, blob_t ip)
"%s: map IP %s to MAC on VLAN %d",
l3_root_dev->session->peername,
addr_print(&auth->addr), l3_if_ndx);
+ dbg_printf("%s\n", auth->status_msg);
+
auth_talk_snmp(auth, l3_root_dev->session, pdu, auth_handle_arp_reply);
}
@@ -1172,7 +1192,7 @@ int main(int argc, char **argv)
init_snmp("squark-auth-snmp");
openlog("squark-auth-snmp", LOG_PID, LOG_DAEMON);
- while ((opt = getopt(argc, argv, "Vc:r:i:R:v:f:T:Ks")) != -1) {
+ while ((opt = getopt(argc, argv, "Vc:r:i:R:v:f:T:KsM:")) != -1) {
switch (opt) {
case 'V':
fprintf(stderr, "squark-auth-snmp %s\n", squark_version);
@@ -1204,6 +1224,12 @@ int main(int argc, char **argv)
case 's':
do_syslog = TRUE;
break;
+ case 'M':
+ if (!addr_parse_prefix(BLOB_STRLEN(optarg), &management_subnet, &management_prefix)) {
+ fprintf(stderr, "'%s' is not a valid network prefix\n", optarg);
+ return 1;
+ }
+ break;
}
}
argc -= optind;