Skip to content

Commit

Permalink
Merge pull request #3 from westermo/send-proxy-queries
Browse files Browse the repository at this point in the history
Send proxy queries in proxy mode
  • Loading branch information
jackuess authored Jun 20, 2022
2 parents f3fac4b + 0b9f1db commit b57aef3
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 85 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,9 @@ Description:
however strongly recommended to leave this setting commented out!

> **Note:** the daemon needs an address on interfaces to operate, it is
> expected that querierd runs on top of a bridge, and that the bridge
> takes care of per-VLAN proxy queries. Also, currently the daemon does
> not react automatically to IP address changes, so it needs to be
> SIGHUP'ed to use any new interface or address.
> expected that querierd runs on top of a bridge. Also, currently the
> daemon does not react automatically to IP address changes, so it needs
> to be SIGHUP'ed to use any new interface or address.

Motivation
Expand Down
28 changes: 28 additions & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,31 @@ struct ifi *config_find_iface(int ifindex)
return NULL;
}

static int getmac(const char *ifname, uint8_t *mac, size_t size)
{
struct ifreq ifr;
int rc = 0;
int sock;

if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
return 1;

ifr.ifr_addr.sa_family = AF_INET;
strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));

if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
rc = 1;
goto done;
}

memcpy(mac, ifr.ifr_hwaddr.sa_data, size);

done:
close(sock);

return rc;
}

/*
* Called by parser to add an interface to start or watch for in the future
*/
Expand Down Expand Up @@ -106,6 +131,9 @@ struct ifi *config_iface_add(char *ifname)
else
ifi->ifi_flags |= IFIF_DOWN;

if (getmac(ifname, ifi->ifi_hwaddr, sizeof(ifi->ifi_hwaddr)))
logit(LOG_WARNING, errno, "failed finding hw address for iface %s", ifname);

TAILQ_INSERT_TAIL(&ifaces, ifi, ifi_link);

return ifi;
Expand Down
8 changes: 7 additions & 1 deletion src/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ extern char s2[MAX_INET_BUF_LEN];
extern char s3[MAX_INET_BUF_LEN];
extern char s4[MAX_INET_BUF_LEN];

#define IGMP_PROXY_QUERY_MAXLEN (sizeof(struct ether_header) + \
sizeof(struct ip) + \
4 + \
IGMP_MINLEN)

/*
* Limit on length of route data
*/
Expand Down Expand Up @@ -145,8 +150,9 @@ extern void resetlogging(void *);
extern void igmp_init(void);
extern void igmp_exit(void);
extern void accept_igmp(int, size_t);
extern size_t build_igmp(uint32_t, uint32_t, int, int, uint32_t, int);
extern size_t build_igmp(uint8_t *, uint32_t, uint32_t, int, int, uint32_t, int);
extern void send_igmp(int, uint32_t, uint32_t, int, int, uint32_t, int);
extern void send_igmp_proxy(const struct ifi *);
extern char * igmp_packet_kind(uint32_t, uint32_t);
extern int igmp_debug_kind(uint32_t, uint32_t);

Expand Down
53 changes: 30 additions & 23 deletions src/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,37 @@ void iface_zero(struct ifi *ifi)
ifi->ifi_igmpv1_warn = 0;
}

static int iface_is_proxy(const struct ifi *ifi)
{
return ifi->ifi_flags & IFIF_DISABLED;
}

/*
* Restart IGMP Querier election
*
* Start by figuring out the best local address for the iface. Check if
* the current address is better (RFC), make sure an IPv4LL doesn't win.
* Usually we want a real address if available. 0.0.0.0 is reserved for
* proxy querys, which we cannot do on a plain UDP socket, and they must
* never win an election. (Proxy queries should be sent by the bridge.)
* proxy querys, which we resort to if proxy mode is active and no real
* querier has been seen.
*/
void iface_check_election(struct ifi *ifi)
{
in_addr_t curr = 0;
struct phaddr *pa;

if (iface_is_proxy(ifi)) {
if (ifi->ifi_querier && ifi->ifi_querier->al_addr)
return;

if (ifi->ifi_querier) {
pev_timer_del(ifi->ifi_querier->al_timerid);
free(ifi->ifi_querier);
ifi->ifi_querier = NULL;
}
goto elected;
}

TAILQ_FOREACH(pa, &ifi->ifi_addrs, pa_link) {
in_addr_t cand = pa->pa_addr;

Expand Down Expand Up @@ -142,7 +159,8 @@ void iface_check_election(struct ifi *ifi)
* the first query.
*/
ifi->ifi_flags |= IFIF_QUERIER;
logit(LOG_DEBUG, 0, "Assuming querier duties on interface %s", ifi->ifi_name);
logit(LOG_DEBUG, 0, "Assuming %squerier duties on interface %s",
iface_is_proxy(ifi) ? "proxy " : "", ifi->ifi_name);
send_query(ifi, allhosts_group, igmp_response_interval * IGMP_TIMER_SCALE, 0);
}

Expand Down Expand Up @@ -269,19 +287,6 @@ static void send_query(struct ifi *ifi, uint32_t dst, int code, uint32_t group)
{
int datalen = 4;

if (!ifi->ifi_curr_addr) {
/*
* If we send with source address 0.0.0.0 on a UDP socket the
* kernel will go dumpster diving to find a "suitable" address
* from another interface. Obviously we don't want that ... we
* would've liked to be able to send a proxy query, but that's
* not possible unless SOCK_RAW, so we delegate the proxy query
* mechanism to the bridge and bail out here.
*/
// logit(LOG_DEBUG, 0, "Skipping send of query on %s, no address yet.", ifi->ifi_name);
return;
}

/*
* IGMP version to send depends on the compatibility mode of the
* interface:
Expand All @@ -297,13 +302,16 @@ static void send_query(struct ifi *ifi, uint32_t dst, int code, uint32_t group)
code = 0;
}

logit(LOG_DEBUG, 0, "Sending %squery on %s",
logit(LOG_DEBUG, 0, "Sending %squery on %s src %s",
(ifi->ifi_flags & IFIF_IGMPV1) ? "v1 " :
(ifi->ifi_flags & IFIF_IGMPV2) ? "v2 " : "v3 ",
ifi->ifi_name);
ifi->ifi_name, inet_name(ifi->ifi_curr_addr, 1));

send_igmp(ifi->ifi_ifindex, ifi->ifi_curr_addr, dst, IGMP_MEMBERSHIP_QUERY,
code, group, datalen);
if (ifi->ifi_curr_addr)
send_igmp(ifi->ifi_ifindex, ifi->ifi_curr_addr, dst, IGMP_MEMBERSHIP_QUERY,
code, group, datalen);
else
send_igmp_proxy(ifi);
}

static void start_iface(struct ifi *ifi)
Expand All @@ -328,8 +336,7 @@ static void start_iface(struct ifi *ifi)
/*
* Check if we should assume the querier role
*/
if (!(ifi->ifi_flags & IFIF_DISABLED))
iface_check_election(ifi);
iface_check_election(ifi);

logit(LOG_INFO, 0, "Interface %s now in service", ifi->ifi_name);
}
Expand Down Expand Up @@ -382,7 +389,7 @@ static void query_groups(int period, void *arg)
{
struct ifi *ifi = (struct ifi *)arg;

if (ifi->ifi_flags & (IFIF_DOWN | IFIF_DISABLED))
if (ifi->ifi_flags & IFIF_DOWN)
return;

if (ifi->ifi_flags & IFIF_QUERIER)
Expand Down
1 change: 1 addition & 0 deletions src/iface.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ struct ifi {
struct listaddr *ifi_querier; /* IGMP querier (one or none) */
int ifi_timerid; /* IGMP query timer */
int ifi_igmpv1_warn; /* To rate-limit IGMPv1 warnings */
uint8_t ifi_hwaddr[6]; /* MAC address of this interface */
};

#define IFIF_DOWN 0x000100 /* kernel state of interface */
Expand Down
Loading

0 comments on commit b57aef3

Please sign in to comment.