summaryrefslogtreecommitdiff
path: root/sys/src/9/ip
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2022-11-20 15:38:36 +0000
committercinap_lenrek <cinap_lenrek@felloff.net>2022-11-20 15:38:36 +0000
commit98a66671cf6900b6239ac1b48638ffa835e75c27 (patch)
treee2e11db47365f6187bcec0bf49fdb86d8790202e /sys/src/9/ip
parent0ed8a3bd7b1bbe2a3857f2abcaabdc4edd2d8b05 (diff)
devip: lilu dallas multicast.
Allow accepting udp "connections" using a multicast local address. Before, it was only possible to receive multicast using the headers option. Having a connection orirented stream can be very usefull when receiving multicast audio data. One gets a "connection" for every source. Implement (optional) support for IGMPv2 and MLDv1. This can be usefull if bridges on the network have IGMP/MLD snooping enabled, and wont forward multicast traffic unless we report what we excpect. This is experimental for now, so the igmp protocol must be manually added to the kernel configuration.
Diffstat (limited to 'sys/src/9/ip')
-rw-r--r--sys/src/9/ip/devip.c18
-rw-r--r--sys/src/9/ip/igmp.c481
-rw-r--r--sys/src/9/ip/ip.h3
-rw-r--r--sys/src/9/ip/ipaux.c3
-rw-r--r--sys/src/9/ip/ipifc.c145
-rw-r--r--sys/src/9/ip/ipv6.h1
-rw-r--r--sys/src/9/ip/netlog.c1
-rw-r--r--sys/src/9/ip/udp.c42
8 files changed, 473 insertions, 221 deletions
diff --git a/sys/src/9/ip/devip.c b/sys/src/9/ip/devip.c
index cb6be1d1c..44800bdb9 100644
--- a/sys/src/9/ip/devip.c
+++ b/sys/src/9/ip/devip.c
@@ -1173,27 +1173,27 @@ ipwrite(Chan* ch, void *v, long n, vlong off)
if(cb->nf < 2)
error("addmulti needs interface address");
if(cb->nf == 2){
- if(!ipismulticast(c->raddr))
- error("addmulti for a non multicast address");
if (parseip(ia, cb->f[1]) == -1)
error(Ebadip);
- ipifcaddmulti(c, c->raddr, ia);
+ if(ipcmp(c->raddr, IPnoaddr) == 0 || ipismulticast(c->laddr))
+ ipifcaddmulti(c, c->laddr, ia);
+ else
+ ipifcaddmulti(c, c->raddr, ia);
} else {
if (parseip(ia, cb->f[1]) == -1 ||
parseip(ma, cb->f[2]) == -1)
error(Ebadip);
- if(!ipismulticast(ma))
- error("addmulti for a non multicast address");
ipifcaddmulti(c, ma, ia);
}
} else if(strcmp(cb->f[0], "remmulti") == 0){
if(cb->nf < 2)
error("remmulti needs interface address");
- if(!ipismulticast(c->raddr))
- error("remmulti for a non multicast address");
if (parseip(ia, cb->f[1]) == -1)
error(Ebadip);
- ipifcremmulti(c, c->raddr, ia);
+ if(ipcmp(c->raddr, IPnoaddr) == 0 || ipismulticast(c->laddr))
+ ipifcremmulti(c, c->laddr, ia);
+ else
+ ipifcremmulti(c, c->raddr, ia);
} else if(x->ctl != nil) {
p = (*x->ctl)(c, cb->f, cb->nf);
if(p != nil)
@@ -1258,7 +1258,7 @@ Fsproto(Fs *f, Proto *p)
p->f = f;
- if(p->ipproto > 0){
+ if(p->ipproto >= 0){
if(f->t2p[p->ipproto] != nil)
return -1;
f->t2p[p->ipproto] = p;
diff --git a/sys/src/9/ip/igmp.c b/sys/src/9/ip/igmp.c
index 9e4b86c79..6122b28e8 100644
--- a/sys/src/9/ip/igmp.c
+++ b/sys/src/9/ip/igmp.c
@@ -1,6 +1,5 @@
/*
- * igmp - internet group management protocol
- * unfinished.
+ * IGMPv2 - internet group management protocol (and MLDv1)
*/
#include "u.h"
#include "../port/lib.h"
@@ -10,6 +9,7 @@
#include "../port/error.h"
#include "ip.h"
+#include "ipv6.h"
enum
{
@@ -19,6 +19,14 @@ enum
IGMPquery = 1,
IGMPreport = 2,
+ IGMPv2report = 6,
+ IGMPv2leave = 7,
+
+ IP_MLDPROTO = HBH, /* hop-by-hop header */
+
+ MLDquery = 130,
+ MLDreport = 131,
+ MLDdone = 132,
MSPTICK = 100,
MAXTIMEOUT = 10000/MSPTICK, /* at most 10 secs for a response */
@@ -36,262 +44,399 @@ struct IGMPpkt
uchar Unused;
uchar proto; /* Protocol */
uchar cksum[2]; /* checksum of ip portion */
- uchar src[IPaddrlen]; /* Ip source */
- uchar dst[IPaddrlen]; /* Ip destination */
+ uchar src[4]; /* Ip source */
+ uchar dst[4]; /* Ip destination */
/* igmp header */
uchar vertype; /* version and type */
- uchar unused;
- uchar igmpcksum[2]; /* checksum of igmp portion */
- uchar group[IPaddrlen]; /* multicast group */
+ uchar resptime;
+ uchar igmpcksum[2]; /* checksum of igmp portion */
+ uchar group[4]; /* multicast group */
uchar payload[];
};
#define IGMPPKTSZ offsetof(IGMPpkt, payload[0])
-/*
- * lists for group reports
- */
-typedef struct IGMPrep IGMPrep;
-struct IGMPrep
-{
- IGMPrep *next;
- Medium *m;
- int ticks;
- Multicast *multi;
-};
+typedef struct MLDpkt MLDpkt;
+struct MLDpkt {
+ IPV6HDR;
-typedef struct IGMP IGMP;
-struct IGMP
-{
- Lock;
- Rendez r;
- IGMPrep *reports;
+ uchar type;
+ uchar code;
+ uchar cksum[2];
+ uchar delay[2];
+ uchar res[2];
+ uchar group[IPaddrlen];
+ uchar payload[];
};
-IGMP igmpalloc;
+#define MLDPKTSZ offsetof(MLDpkt, payload[0])
+
+static uchar mldhbhopt[] = {
+ ICMPv6, /* NextHeader */
+ 0x00, /* HeaderLength */
+ 0x05, /* Option: Router Alert */
+ 0x02, /* Length */
+ 0x00, 0x00, /* MLD */
- Proto igmp;
-extern Fs fs;
+ 0x01, /* Option: PadN */
+ 0x00, /* Length */
+};
-static struct Stats
+typedef struct Report Report;
+struct Report
{
- ulong inqueries;
- ulong outqueries;
- ulong inreports;
- ulong outreports;
-} stats;
+ Report *next;
+ Proto *proto;
+ Ipifc *ifc;
+ int ifcid;
+ int timeout; /* in MSPTICK's */
+ Ipmulti *multi;
+};
-void
-igmpsendreport(Medium *m, uchar *addr)
+typedef struct Priv Priv;
+struct Priv
+{
+ QLock;
+ Rendez r;
+ Report *reports;
+};
+
+static void
+igmpsendreport(Fs *f, uchar *src, uchar *dst, uchar *group, int done)
{
IGMPpkt *p;
Block *bp;
- bp = allocb(sizeof(IGMPpkt));
- p = (IGMPpkt*)bp->wp;
- p->vihl = IP_VER4;
+ bp = allocb(IGMPPKTSZ);
bp->wp += IGMPPKTSZ;
- memset(bp->rp, 0, IGMPPKTSZ);
- hnputl(p->src, Mediumgetaddr(m));
- hnputl(p->dst, Ipallsys);
- p->vertype = (1<<4) | IGMPreport;
+ p = (IGMPpkt*)bp->rp;
+ memset(p, 0, IGMPPKTSZ);
+ p->vihl = IP_VER4;
+ memmove(p->src, src+IPv4off, IPv4addrlen);
+ memmove(p->dst, dst+IPv4off, IPv4addrlen);
+ p->vertype = (1<<4) | (done? IGMPv2leave: IGMPv2report);
+ p->resptime = 0;
p->proto = IP_IGMPPROTO;
- memmove(p->group, addr, IPaddrlen);
+ memmove(p->group, group+IPv4off, IPv4addrlen);
hnputs(p->igmpcksum, ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE));
- netlog(Logigmp, "igmpreport %I\n", p->group);
- stats.outreports++;
- ipoput4(bp, 0, 1, DFLTTOS, nil); /* TTL of 1 */
+ ipoput4(f, bp, 0, 1, DFLTTOS, nil); /* TTL of 1 */
+}
+
+static void
+mldsendreport(Fs *f, uchar *src, uchar *dst, uchar *group, int done)
+{
+ MLDpkt *p;
+ Block *bp;
+
+ if(!islinklocal(src))
+ return;
+
+ bp = allocb(sizeof(mldhbhopt)+MLDPKTSZ);
+ bp->wp += sizeof(mldhbhopt)+MLDPKTSZ;
+ bp->rp += sizeof(mldhbhopt);
+ p = (MLDpkt*)bp->rp;
+ memset(p, 0, MLDPKTSZ);
+ ipmove(p->src, src);
+ ipmove(p->dst, dst);
+ p->type = done? MLDdone: MLDreport;
+ p->code = 0;
+ ipmove(p->group, group);
+
+ /* generate checksum */
+ hnputl(p->vcf, 0);
+ hnputs(p->ploadlen, MLDPKTSZ-IP6HDR);
+ p->proto = 0;
+ p->ttl = ICMPv6; /* ttl gets set later */
+ hnputs(p->cksum, 0);
+ hnputs(p->cksum, ptclcsum(bp, 0, MLDPKTSZ));
+
+ /* add hop-by-hop option header */
+ bp->rp -= sizeof(mldhbhopt);
+ memmove(bp->rp, p, IP6HDR);
+ memmove(bp->rp + IP6HDR, mldhbhopt, sizeof(mldhbhopt));
+ p = (MLDpkt*)bp->rp;
+ p->proto = IP_MLDPROTO;
+ hnputs(p->ploadlen, BLEN(bp) - IP6HDR);
+
+ ipoput6(f, bp, 0, 1, DFLTTOS, nil); /* TTL of 1 */
+}
+
+static void
+sendreport(Proto *pr, uchar *ia, uchar *group, int done)
+{
+ switch(pr->ipproto){
+ case IP_IGMPPROTO:
+ igmpsendreport(pr->f, ia, group, group, done);
+ break;
+ case IP_MLDPROTO:
+ mldsendreport(pr->f, ia, group, group, done);
+ break;
+ }
}
static int
isreport(void *a)
{
- USED(a);
- return igmpalloc.reports != 0;
+ return ((Priv*)a)->reports != 0;
}
-
-void
+static void
igmpproc(void *a)
{
- IGMPrep *rp, **lrp;
- Multicast *mp, **lmp;
- uchar ip[IPaddrlen];
-
- USED(a);
+ Proto *pr, *igmp = a;
+ Priv *priv = igmp->priv;
+ Report *rp, **lrp;
+ Ipmulti *mp, **lmp;
for(;;){
- sleep(&igmpalloc.r, isreport, 0);
+ sleep(&priv->r, isreport, priv);
for(;;){
- lock(&igmpalloc);
-
- if(igmpalloc.reports == nil)
+ qlock(priv);
+ if(priv->reports == nil)
break;
/* look for a single report */
- lrp = &igmpalloc.reports;
mp = nil;
- for(rp = *lrp; rp; rp = *lrp){
- rp->ticks++;
+ pr = nil;
+ lrp = &priv->reports;
+ for(rp = *lrp; rp != nil; rp = *lrp){
lmp = &rp->multi;
- for(mp = *lmp; mp; mp = *lmp){
- if(rp->ticks >= mp->timeout){
+ for(mp = *lmp; mp != nil; mp = *lmp){
+ if(rp->timeout <= 1 || nrand(rp->timeout) == 0){
*lmp = mp->next;
break;
}
lmp = &mp->next;
}
- if(mp != nil)
- break;
-
+ pr = rp->proto;
if(rp->multi != nil){
+ rp->timeout--;
lrp = &rp->next;
- continue;
} else {
*lrp = rp->next;
free(rp);
}
+ if(mp != nil)
+ break;
}
- unlock(&igmpalloc);
+ qunlock(priv);
- if(mp){
+ if(mp != nil){
/* do a single report and try again */
- hnputl(ip, mp->addr);
- igmpsendreport(rp->m, ip);
+ if(pr != nil && !waserror()){
+ sendreport(pr, mp->ia, mp->ma, 0);
+ poperror();
+ }
free(mp);
continue;
}
tsleep(&up->sleep, return0, 0, MSPTICK);
}
- unlock(&igmpalloc);
+ qunlock(priv);
}
+}
+
+/*
+ * find report list for this protocol and interface
+ */
+static Report*
+findreport(Report *rp, Proto *pr, Ipifc *ifc)
+{
+ for(; rp != nil; rp = rp->next)
+ if(rp->proto == pr && rp->ifc == ifc && rp->ifcid == ifc->ifcid)
+ return rp;
+ return nil;
}
-void
-igmpiput(Medium *m, Ipifc *, Block *bp)
+static void
+queuereport(Proto *pr, Ipifc *ifc, uchar *group, int timeout)
{
- int n;
- IGMPpkt *ghp;
- Ipaddr group;
- IGMPrep *rp, **lrp;
- Multicast *mp, **lmp;
-
- ghp = (IGMPpkt*)(bp->rp);
- netlog(Logigmp, "igmpiput: %d %I\n", ghp->vertype, ghp->group);
-
- n = blocklen(bp);
- if(n < IGMP_IPHDRSIZE+IGMP_HDRSIZE){
- netlog(Logigmp, "igmpiput: bad len\n");
- goto error;
- }
- if((ghp->vertype>>4) != 1){
- netlog(Logigmp, "igmpiput: bad igmp type\n");
- goto error;
- }
- if(ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE)){
- netlog(Logigmp, "igmpiput: checksum error %I\n", ghp->src);
- goto error;
- }
+ Priv *priv = pr->priv;
+ Report *rp;
- group = nhgetl(ghp->group);
-
- lock(&igmpalloc);
- switch(ghp->vertype & 0xf){
- case IGMPquery:
+ qlock(priv);
+ if(findreport(priv->reports, pr, ifc) != nil){
/*
- * start reporting groups that we're a member of.
+ * we are already reporting on this interface,
+ * wait for the report to time-out.
*/
- stats.inqueries++;
- for(rp = igmpalloc.reports; rp; rp = rp->next)
- if(rp->m == m)
- break;
- if(rp != nil)
- break; /* already reporting */
-
- mp = Mediumcopymulti(m);
- if(mp == nil)
- break;
-
- rp = malloc(sizeof(*rp));
- if(rp == nil)
- break;
+ qunlock(priv);
+ return;
+ }
- rp->m = m;
- rp->multi = mp;
- rp->ticks = 0;
- for(; mp; mp = mp->next)
- mp->timeout = nrand(MAXTIMEOUT);
- rp->next = igmpalloc.reports;
- igmpalloc.reports = rp;
+ /*
+ * start reporting groups that we're a member of.
+ */
+ rp = smalloc(sizeof(Report));
+ rp->proto = pr;
+ rp->ifc = ifc;
+ rp->ifcid = ifc->ifcid;
+ rp->timeout = (timeout < 1 || timeout > MAXTIMEOUT) ? MAXTIMEOUT : timeout;
+ rp->multi = ipifcgetmulti(pr->f, ifc, group);
+
+ rp->next = priv->reports;
+ priv->reports = rp;
+
+ wakeup(&priv->r);
+ qunlock(priv);
+}
- wakeup(&igmpalloc.r);
+static void
+purgereport(Proto *pr, Ipifc *ifc, uchar *group)
+{
+ Priv *priv = pr->priv;
+ Report *rp;
- break;
- case IGMPreport:
- /*
- * find report list for this medium
- */
- stats.inreports++;
- lrp = &igmpalloc.reports;
- for(rp = *lrp; rp; rp = *lrp){
- if(rp->m == m)
- break;
- lrp = &rp->next;
- }
- if(rp == nil)
- break;
+ qlock(priv);
+ if((rp = findreport(priv->reports, pr, ifc)) != nil){
+ Ipmulti *mp, **lmp;
- /*
- * if someone else has reported a group,
- * we don't have to.
- */
lmp = &rp->multi;
for(mp = *lmp; mp; mp = *lmp){
- if(mp->addr == group){
+ if(ipcmp(mp->ma, group) == 0){
*lmp = mp->next;
free(mp);
break;
}
lmp = &mp->next;
}
+ }
+ qunlock(priv);
+}
+static void
+mldiput(Proto *mld, Ipifc *ifc, Block *bp)
+{
+ MLDpkt *p;
+ uchar *opt, *payload;
+
+ p = (MLDpkt*)(bp->rp);
+ /* check we have the hop-by-hop header */
+ if((p->vcf[0] & 0xF0) != IP_VER6 || p->proto != IP_MLDPROTO)
+ goto error;
+ if(p->ttl != 1 || !isv6mcast(p->dst) || !islinklocal(p->src))
+ goto error;
+
+ /* strip the hop-by-hop option header */
+ if(BLEN(bp) < IP6HDR+sizeof(mldhbhopt))
+ goto error;
+ opt = bp->rp + IP6HDR;
+ if(opt[0] != ICMPv6)
+ goto error;
+ payload = opt + ((int)opt[1] + 1)*8;
+ if(payload >= bp->wp)
+ goto error;
+ if(memcmp(opt+2, mldhbhopt+2, 4) != 0)
+ goto error;
+ memmove(payload-IP6HDR, bp->rp, IP6HDR);
+ bp->rp = payload - IP6HDR;
+ if(BLEN(bp) < MLDPKTSZ)
+ goto error;
+ p = (MLDpkt*)bp->rp;
+
+ /* verify ICMPv6 checksum */
+ hnputl(p->vcf, 0); /* borrow IP header as pseudoheader */
+ p->ttl = ICMPv6;
+ p->proto = 0;
+ hnputs(p->ploadlen, MLDPKTSZ-IP6HDR);
+ if(ptclcsum(bp, 0, MLDPKTSZ))
+ goto error;
+
+ if(ipcmp(p->group, IPnoaddr) != 0 && ipismulticast(p->group) != V6)
+ goto error;
+
+ switch(p->type){
+ case MLDquery:
+ queuereport(mld, ifc, p->group, nhgets(p->delay)/MSPTICK);
+ break;
+ case MLDreport:
+ purgereport(mld, ifc, p->group);
break;
}
- unlock(&igmpalloc);
+error:
+ freeblist(bp);
+}
+
+static void
+igmpiput(Proto *igmp, Ipifc *ifc, Block *bp)
+{
+ uchar group[IPaddrlen];
+ IGMPpkt *p;
+ p = (IGMPpkt*)bp->rp;
+ if((p->vihl & 0xF0) != IP_VER4)
+ goto error;
+ if(BLEN(bp) < IGMP_IPHDRSIZE+IGMP_HDRSIZE)
+ goto error;
+ if((p->vertype>>4) != 1)
+ goto error;
+ if(ptclcsum(bp, IGMP_IPHDRSIZE, IGMP_HDRSIZE))
+ goto error;
+
+ v4tov6(group, p->group);
+ if(ipcmp(group, v4prefix) != 0 && ipismulticast(group) != V4)
+ goto error;
+
+ switch(p->vertype & 0xF){
+ case IGMPquery:
+ queuereport(igmp, ifc, group, p->resptime);
+ break;
+ case IGMPreport:
+ case IGMPv2report:
+ purgereport(igmp, ifc, group);
+ break;
+ }
error:
- freeb(bp);
+ freeblist(bp);
}
-int
-igmpstats(char *buf, int len)
+static void
+multicastreport(Fs *f, Ipifc *ifc, uchar *ma, uchar *ia, int done)
{
- return snprint(buf, len, "\trcvd %d %d\n\tsent %d %d\n",
- stats.inqueries, stats.inreports,
- stats.outqueries, stats.outreports);
+ Proto *pr = Fsrcvpcolx(f, isv4(ma)? IP_IGMPPROTO: IP_MLDPROTO);
+ purgereport(pr, ifc, ma);
+ sendreport(pr, ia, ma, done);
}
void
-igmpinit(Fs *fs)
+igmpinit(Fs *f)
{
- igmp.name = "igmp";
- igmp.connect = nil;
- igmp.announce = nil;
- igmp.ctl = nil;
- igmp.state = nil;
- igmp.close = nil;
- igmp.rcv = igmpiput;
- igmp.stats = igmpstats;
- igmp.ipproto = IP_IGMPPROTO;
- igmp.nc = 0;
- igmp.ptclsize = 0;
-
- igmpreportfn = igmpsendreport;
- kproc("igmpproc", igmpproc, 0);
-
- Fsproto(fs, &igmp);
+ Proto *igmp, *mld;
+
+ igmp = smalloc(sizeof(Proto));
+ igmp->priv = smalloc(sizeof(Priv));
+ igmp->name = "igmp";
+ igmp->connect = nil;
+ igmp->announce = nil;
+ igmp->ctl = nil;
+ igmp->state = nil;
+ igmp->close = nil;
+ igmp->rcv = igmpiput;
+ igmp->stats = nil;
+ igmp->ipproto = IP_IGMPPROTO;
+ igmp->nc = 0;
+ igmp->ptclsize = 0;
+ Fsproto(f, igmp);
+
+ mld = smalloc(sizeof(Proto));
+ mld->priv = igmp->priv;
+ mld->name = "mld";
+ mld->connect = nil;
+ mld->announce = nil;
+ mld->ctl = nil;
+ mld->state = nil;
+ mld->close = nil;
+ mld->rcv = mldiput;
+ mld->stats = nil;
+ mld->ipproto = IP_MLDPROTO;
+ mld->nc = 0;
+ mld->ptclsize = 0;
+ Fsproto(f, mld);
+
+ multicastreportfn = multicastreport;
+ kproc("igmpproc", igmpproc, igmp);
}
diff --git a/sys/src/9/ip/ip.h b/sys/src/9/ip/ip.h
index bf4b2cea3..a75d59b89 100644
--- a/sys/src/9/ip/ip.h
+++ b/sys/src/9/ip/ip.h
@@ -725,6 +725,7 @@ extern int ipv6local(Ipifc *ifc, uchar *local, int prefixlen, uchar *remote);
extern Iplifc* iplocalonifc(Ipifc *ifc, uchar *ip);
extern Iplifc* ipremoteonifc(Ipifc *ifc, uchar *ip);
extern int ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip);
+extern Ipmulti* ipifcgetmulti(Fs *f, Ipifc *ifc, uchar *ma);
extern void ipifcremmulti(Conv *c, uchar *ma, uchar *ia);
extern void ipifcaddmulti(Conv *c, uchar *ma, uchar *ia);
extern char* ipifcrem(Ipifc *ifc, char **argv, int argc);
@@ -777,4 +778,4 @@ extern Chan* chandial(char*, char*, char*, Chan**);
/*
* global to all of the stack
*/
-extern void (*igmpreportfn)(Ipifc*, uchar*);
+extern void (*multicastreportfn)(Fs*, Ipifc*, uchar*, uchar*, int);
diff --git a/sys/src/9/ip/ipaux.c b/sys/src/9/ip/ipaux.c
index 731176782..bd4be1349 100644
--- a/sys/src/9/ip/ipaux.c
+++ b/sys/src/9/ip/ipaux.c
@@ -196,8 +196,7 @@ enum
void
ipv62smcast(uchar *smcast, uchar *a)
{
- assert(IPaddrlen == 16);
- memmove(smcast, v6solicitednode, IPaddrlen);
+ ipmove(smcast, v6solicitednode);
smcast[13] = a[13];
smcast[14] = a[14];
smcast[15] = a[15];
diff --git a/sys/src/9/ip/ipifc.c b/sys/src/9/ip/ipifc.c
index f4786d6fa..ea108a19b 100644
--- a/sys/src/9/ip/ipifc.c
+++ b/sys/src/9/ip/ipifc.c
@@ -19,6 +19,7 @@ enum {
};
Medium *media[Maxmedia] = { 0 };
+void (*multicastreportfn)(Fs*, Ipifc*, uchar*, uchar*, int);
/*
* cache of local addresses (addresses we answer to)
@@ -40,18 +41,6 @@ struct Ipselftab
Ipself *hash[NHASH]; /* hash chains */
};
-/*
- * Multicast addresses are chained onto a Chan so that
- * we can remove them when the Chan is closed.
- */
-typedef struct Ipmcast Ipmcast;
-struct Ipmcast
-{
- Ipmcast *next;
- uchar ma[IPaddrlen]; /* multicast address */
- uchar ia[IPaddrlen]; /* interface address */
-};
-
/* quick hash for ip addresses */
#define hashipa(a) (((a)[IPaddrlen-2] + (a)[IPaddrlen-1])%NHASH)
@@ -611,6 +600,9 @@ ipifcadd(Ipifc *ifc, char **argv, int argc, int tentative, Iplifc *lifcp)
addselfcache(f, ifc, lifc, bcast, Rbcast);
addselfcache(f, ifc, lifc, IPv4bcast, Rbcast);
+
+ /* add all nodes multicast address */
+ addselfcache(f, ifc, lifc, IPv4allsys, Rmulti);
} else {
if(ipcmp(ip, v6loopback) == 0) {
/* add node-local mcast address */
@@ -684,11 +676,11 @@ ipifcremlifc(Ipifc *ifc, Iplifc **l)
/* remove route for all nodes multicast */
if((lifc->type & Rv4) == 0){
- if(ipcmp(lifc->local, v6loopback) == 0)
+ if(ipcmp(lifc->local, v6loopback) == 0){
remroute(f, v6allnodesN, v6allnodesNmask,
lifc->local, IPallbits,
v6allnodesN, Rmulti, ifc, tifc);
-
+ }
remroute(f, v6allnodesL, v6allnodesLmask,
lifc->local, IPallbits,
v6allnodesL, Rmulti, ifc, tifc);
@@ -960,8 +952,12 @@ addselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a, int type)
IPallbits : IPnoaddr,
a, type, ifc, tifc);
- if((type & Rmulti) && ifc->m->addmulti != nil)
- (*ifc->m->addmulti)(ifc, a, lifc->local);
+ if(type & Rmulti){
+ if(ifc->m->addmulti != nil)
+ (*ifc->m->addmulti)(ifc, a, lifc->local);
+ if(multicastreportfn != nil)
+ (*multicastreportfn)(f, ifc, a, lifc->local, 0);
+ }
} else
lp->ref++;
@@ -1072,6 +1068,26 @@ remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a)
if(--(link->ref) != 0)
goto out;
+ /* ref == 0, remove from both chains and free the link */
+ *l_lifc = link->lifclink;
+ *l_self = link->selflink;
+ iplinkfree(link);
+
+ if((p->type & Rmulti) != 0){
+ if(multicastreportfn != nil){
+ if(!waserror()){
+ (*multicastreportfn)(f, ifc, a, lifc->local, 1);
+ poperror();
+ }
+ }
+ if(ifc->m->remmulti != nil){
+ if(!waserror()){
+ (*ifc->m->remmulti)(ifc, a, lifc->local);
+ poperror();
+ }
+ }
+ }
+
/* remove from routing table */
remroute(f, a, IPallbits,
lifc->local,
@@ -1079,18 +1095,6 @@ remselfcache(Fs *f, Ipifc *ifc, Iplifc *lifc, uchar *a)
IPallbits : IPnoaddr,
a, p->type, ifc, tifc);
- if((p->type & Rmulti) && ifc->m->remmulti != nil){
- if(!waserror()){
- (*ifc->m->remmulti)(ifc, a, lifc->local);
- poperror();
- }
- }
-
- /* ref == 0, remove from both chains and free the link */
- *l_lifc = link->lifclink;
- *l_self = link->selflink;
- iplinkfree(link);
-
if(p->link != nil)
goto out;
@@ -1423,6 +1427,20 @@ ipremoteonifc(Ipifc *ifc, uchar *ip)
return nil;
}
+/*
+ * return link-local interface
+ */
+Iplifc*
+iplinklocalifc(Ipifc *ifc)
+{
+ Iplifc *lifc;
+
+ for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next)
+ if(islinklocal(lifc->local))
+ return lifc;
+
+ return nil;
+}
/*
* See if we're proxying for this address on this interface
@@ -1441,6 +1459,65 @@ ipproxyifc(Fs *f, Ipifc *ifc, uchar *ip)
}
/*
+ * Return a copy of the multicast addresses on the
+ * specified interface and multicast address.
+ */
+Ipmulti*
+ipifcgetmulti(Fs *f, Ipifc *ifc, uchar *ma)
+{
+ Ipmulti *head, *tail, *mcast;
+ Iplifc *lifc;
+ Iplink *link;
+ Ipself *self;
+ int type;
+
+ type = Rmulti;
+ if(isv4(ma))
+ type |= Rv4;
+ if(ipismulticast(ma) == ((type & Rv4)? V4: V6)){
+ if(ipforme(f, ma) != Rmulti)
+ return nil;
+ } else {
+ ma = nil;
+ }
+
+ head = tail = nil;
+ for(lifc = ifc->lifc; lifc != nil; lifc = lifc->next) {
+ if((type & Rv4) == 0 && !islinklocal(lifc->local))
+ continue;
+
+ for(link = lifc->link; link != nil; link = link->lifclink) {
+ self = link->self;
+ if((self->type & (Rv4|Rmulti)) != type)
+ continue;
+
+ if(ma != nil){
+ if(ipcmp(self->a, ma) != 0)
+ continue;
+ } else {
+ if(ipcmp(self->a, (type & Rv4)? IPv4allsys: v6allnodesL) == 0)
+ continue;
+ }
+
+ mcast = smalloc(sizeof(Ipmulti));
+ mcast->next = nil;
+ ipmove(mcast->ma, self->a);
+ ipmove(mcast->ia, lifc->local);
+
+ if(ma != nil)
+ return mcast;
+
+ if(head == nil)
+ head = mcast;
+ else
+ tail->next = mcast;
+ tail = mcast;
+ }
+ }
+ return head;
+}
+
+/*
* add a multicast address to an interface.
*/
void
@@ -1451,6 +1528,8 @@ ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)
Ipifc *ifc;
Fs *f;
+ if(!ipismulticast(ma))
+ error("addmulti for a non multicast address");
if(isv4(ma) != isv4(ia))
error("incompatible multicast/interface ip address");
@@ -1467,6 +1546,11 @@ ipifcaddmulti(Conv *c, uchar *ma, uchar *ia)
}
if((lifc = iplocalonifc(ifc, ia)) != nil)
addselfcache(f, ifc, lifc, ma, Rmulti);
+
+ /* for IPv6, must add also add to the link-local address */
+ if(!isv4(ia) && !islinklocal(ia) && (lifc = iplinklocalifc(ifc)) != nil)
+ addselfcache(f, ifc, lifc, ma, Rmulti);
+
runlock(ifc);
poperror();
}
@@ -1509,6 +1593,11 @@ ipifcremmulti(Conv *c, uchar *ma, uchar *ia)
remselfcache(f, ifc, lifc, ma);
poperror();
}
+ if(!isv4(ia) && !islinklocal(ia) && !waserror()){
+ if((lifc = iplinklocalifc(ifc)) != nil)
+ remselfcache(f, ifc, lifc, ma);
+ poperror();
+ }
runlock(ifc);
}
free(multi);
diff --git a/sys/src/9/ip/ipv6.h b/sys/src/9/ip/ipv6.h
index 145962b76..b4ad9c5cd 100644
--- a/sys/src/9/ip/ipv6.h
+++ b/sys/src/9/ip/ipv6.h
@@ -78,7 +78,6 @@ enum {
/* various flags & constants */
v6MINTU = 1280,
- HOP_LIMIT = 255,
IP6HDR = 40, /* sizeof(Ip6hdr) = 8 + 2*16 */
IP6FHDR = 8, /* sizeof(Fraghdr6) */
diff --git a/sys/src/9/ip/netlog.c b/sys/src/9/ip/netlog.c
index ea155fe8f..55cf12041 100644
--- a/sys/src/9/ip/netlog.c
+++ b/sys/src/9/ip/netlog.c
@@ -42,6 +42,7 @@ static Netlogflag flags[] =
{ "il", Logil, },
{ "tcp", Logtcp, },
{ "icmp", Logicmp, },
+ { "igmp", Logigmp, },
{ "udp", Logudp, },
{ "compress", Logcompress, },
{ "logilmsg", Logilmsg, },
diff --git a/sys/src/9/ip/udp.c b/sys/src/9/ip/udp.c
index 651e616df..a1db5713c 100644
--- a/sys/src/9/ip/udp.c
+++ b/sys/src/9/ip/udp.c
@@ -100,6 +100,9 @@ typedef struct Udpcb Udpcb;
struct Udpcb
{
uchar headers;
+
+ /* source ip used for transmission */
+ uchar srcip[IPaddrlen];
};
static char*
@@ -113,6 +116,7 @@ udpconnect(Conv *c, char **argv, int argc)
Fsconnected(c, e);
if(e != nil)
return e;
+ ipmove(((Udpcb*)c->ptcl)->srcip, c->laddr);
iphtadd(&upriv->ht, c);
return nil;
}
@@ -139,6 +143,7 @@ udpannounce(Conv *c, char** argv, int argc)
if(e != nil)
return e;
Fsconnected(c, nil);
+ ipmove(((Udpcb*)c->ptcl)->srcip, c->laddr);
iphtadd(&upriv->ht, c);
return nil;
}
@@ -170,6 +175,7 @@ udpclose(Conv *c)
ucb = (Udpcb*)c->ptcl;
ucb->headers = 0;
+ ipmove(ucb->srcip, IPnoaddr);
}
void
@@ -214,6 +220,7 @@ udpkick(void *x, Block *bp)
bp->rp += 2+2; /* Ignore local port */
break;
default:
+ ipmove(laddr, IPnoaddr);
rport = 0;
break;
}
@@ -225,6 +232,7 @@ udpkick(void *x, Block *bp)
version = V6;
} else {
version = convipvers(c);
+ ipmove(laddr, ucb->srcip);
}
dlen = blocklen(bp);
@@ -243,16 +251,13 @@ udpkick(void *x, Block *bp)
if(ucb->headers) {
v6tov4(uh4->udpdst, raddr);
hnputs(uh4->udpdport, rport);
- v6tov4(uh4->udpsrc, laddr);
rh = nil;
} else {
v6tov4(uh4->udpdst, c->raddr);
hnputs(uh4->udpdport, c->rport);
- if(ipcmp(c->laddr, IPnoaddr) == 0)
- findlocalip(f, c->laddr, c->raddr);
- v6tov4(uh4->udpsrc, c->laddr);
rh = c;
}
+ v6tov4(uh4->udpsrc, laddr);
hnputs(uh4->udpsport, c->lport);
hnputs(uh4->udplen, ptcllen);
uh4->udpcksum[0] = 0;
@@ -279,16 +284,13 @@ udpkick(void *x, Block *bp)
if(ucb->headers) {
ipmove(uh6->udpdst, raddr);
hnputs(uh6->udpdport, rport);
- ipmove(uh6->udpsrc, laddr);
rh = nil;
} else {
ipmove(uh6->udpdst, c->raddr);
hnputs(uh6->udpdport, c->rport);
- if(ipcmp(c->laddr, IPnoaddr) == 0)
- findlocalip(f, c->laddr, c->raddr);
- ipmove(uh6->udpsrc, c->laddr);
rh = c;
}
+ ipmove(uh6->udpsrc, laddr);
hnputs(uh6->udpsport, c->lport);
hnputs(uh6->udplen, ptcllen);
uh6->udpcksum[0] = 0;
@@ -394,6 +396,7 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp)
qlock(udp);
iph = iphtlook(&upriv->ht, raddr, rport, laddr, lport);
if(iph == nil){
+Noconv:
/* no conversation found */
upriv->ustats.udpNoPorts++;
qunlock(udp);
@@ -438,17 +441,32 @@ udpiput(Proto *udp, Ipifc *ifc, Block *bp)
if(c->state == Announced){
if(ucb->headers == 0){
- /* create a new conversation */
- if(ipforme(f, laddr) != Runi)
- ipv6local(ifc, laddr, 0, raddr);
+ uchar srcip[IPaddrlen];
+
+ switch(ipforme(f, laddr)){
+ default:
+ goto Noconv;
+ case Runi:
+ ipmove(srcip, laddr);
+ break;
+ case Rmulti:
+ case Rbcast:
+ /*
+ * use the multicast address for reception,
+ * and local unicast ip for transmission.
+ */
+ ipv6local(ifc, srcip, 0, raddr);
+ break;
+ }
c = Fsnewcall(c, raddr, rport, laddr, lport, version);
if(c == nil){
qunlock(udp);
freeblist(bp);
return;
}
- iphtadd(&upriv->ht, c);
ucb = (Udpcb*)c->ptcl;
+ ipmove(ucb->srcip, srcip);
+ iphtadd(&upriv->ht, c);
}
}