vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

gre.c (5099B)


      1 /*
      2  * Generic Routing Encapsulation over IPv4, rfc1702
      3  */
      4 #include "u.h"
      5 #include "lib.h"
      6 #include "mem.h"
      7 #include "dat.h"
      8 #include "fns.h"
      9 #include "error.h"
     10 
     11 #include "ip.h"
     12 
     13 enum
     14 {
     15 	GRE_IPONLY	= 12,		/* size of ip header */
     16 	GRE_IPPLUSGRE	= 12,		/* minimum size of GRE header */
     17 	IP_GREPROTO	= 47,
     18 
     19 	GRErxms		= 200,
     20 	GREtickms	= 100,
     21 	GREmaxxmit	= 10,
     22 };
     23 
     24 typedef struct GREhdr
     25 {
     26 	/* ip header */
     27 	uchar	vihl;		/* Version and header length */
     28 	uchar	tos;		/* Type of service */
     29 	uchar	len[2];		/* packet length (including headers) */
     30 	uchar	id[2];		/* Identification */
     31 	uchar	frag[2];	/* Fragment information */
     32 	uchar	Unused;	
     33 	uchar	proto;		/* Protocol */
     34 	uchar	cksum[2];	/* checksum */
     35 	uchar	src[4];		/* Ip source */
     36 	uchar	dst[4];		/* Ip destination */
     37 
     38 	/* gre header */
     39 	uchar	flags[2];
     40 	uchar	eproto[2];	/* encapsulation protocol */
     41 } GREhdr;
     42 
     43 typedef struct GREpriv GREpriv;
     44 struct GREpriv
     45 {
     46 	int		raw;			/* Raw GRE mode */
     47 
     48 	/* non-MIB stats */
     49 	ulong		csumerr;		/* checksum errors */
     50 	ulong		lenerr;			/* short packet */
     51 };
     52 
     53 static void grekick(void *x, Block *bp);
     54 
     55 static char*
     56 greconnect(Conv *c, char **argv, int argc)
     57 {
     58 	Proto *p;
     59 	char *err;
     60 	Conv *tc, **cp, **ecp;
     61 
     62 	err = Fsstdconnect(c, argv, argc);
     63 	if(err != nil)
     64 		return err;
     65 
     66 	/* make sure noone's already connected to this other sys */
     67 	p = c->p;
     68 	QLOCK(p);
     69 	ecp = &p->conv[p->nc];
     70 	for(cp = p->conv; cp < ecp; cp++){
     71 		tc = *cp;
     72 		if(tc == nil)
     73 			break;
     74 		if(tc == c)
     75 			continue;
     76 		if(tc->rport == c->rport && ipcmp(tc->raddr, c->raddr) == 0){
     77 			err = "already connected to that addr/proto";
     78 			ipmove(c->laddr, IPnoaddr);
     79 			ipmove(c->raddr, IPnoaddr);
     80 			break;
     81 		}
     82 	}
     83 	QUNLOCK(p);
     84 
     85 	if(err != nil)
     86 		return err;
     87 	Fsconnected(c, nil);
     88 
     89 	return nil;
     90 }
     91 
     92 static void
     93 grecreate(Conv *c)
     94 {
     95 	c->rq = qopen(64*1024, Qmsg, 0, c);
     96 	c->wq = qbypass(grekick, c);
     97 }
     98 
     99 static int
    100 grestate(Conv *c, char *state, int n)
    101 {
    102 	USED(c);
    103 	return snprint(state, n, "%s\n", "Datagram");
    104 }
    105 
    106 static char*
    107 greannounce(Conv* _, char** __, int ___)
    108 {
    109 	return "pktifc does not support announce";
    110 }
    111 
    112 static void
    113 greclose(Conv *c)
    114 {
    115 	qclose(c->rq);
    116 	qclose(c->wq);
    117 	qclose(c->eq);
    118 	ipmove(c->laddr, IPnoaddr);
    119 	ipmove(c->raddr, IPnoaddr);
    120 	c->lport = 0;
    121 	c->rport = 0;
    122 }
    123 
    124 int drop;
    125 
    126 static void
    127 grekick(void *x, Block *bp)
    128 {
    129 	Conv *c = x;
    130 	GREhdr *ghp;
    131 	uchar laddr[IPaddrlen], raddr[IPaddrlen];
    132 
    133 	if(bp == nil)
    134 		return;
    135 
    136 	/* Make space to fit ip header (gre header already there) */
    137 	bp = padblock(bp, GRE_IPONLY);
    138 	if(bp == nil)
    139 		return;
    140 
    141 	/* make sure the message has a GRE header */
    142 	bp = pullupblock(bp, GRE_IPONLY+GRE_IPPLUSGRE);
    143 	if(bp == nil)
    144 		return;
    145 
    146 	ghp = (GREhdr *)(bp->rp);
    147 	ghp->vihl = IP_VER4;
    148 
    149 	if(!((GREpriv*)c->p->priv)->raw){
    150 		v4tov6(raddr, ghp->dst);
    151 		if(ipcmp(raddr, v4prefix) == 0)
    152 			memmove(ghp->dst, c->raddr + IPv4off, IPv4addrlen);
    153 		v4tov6(laddr, ghp->src);
    154 		if(ipcmp(laddr, v4prefix) == 0){
    155 			if(ipcmp(c->laddr, IPnoaddr) == 0)
    156 				findlocalip(c->p->f, c->laddr, raddr); /* pick interface closest to dest */
    157 			memmove(ghp->src, c->laddr + IPv4off, IPv4addrlen);
    158 		}
    159 		hnputs(ghp->eproto, c->rport);
    160 	}
    161 
    162 	ghp->proto = IP_GREPROTO;
    163 	ghp->frag[0] = 0;
    164 	ghp->frag[1] = 0;
    165 
    166 	ipoput4(c->p->f, bp, 0, c->ttl, c->tos, nil);
    167 }
    168 
    169 static void
    170 greiput(Proto *gre, Ipifc* __, Block *bp)
    171 {
    172 	int len;
    173 	GREhdr *ghp;
    174 	Conv *c, **p;
    175 	ushort eproto;
    176 	uchar raddr[IPaddrlen];
    177 	GREpriv *gpriv;
    178 
    179 	gpriv = gre->priv;
    180 	ghp = (GREhdr*)(bp->rp);
    181 
    182 	v4tov6(raddr, ghp->src);
    183 	eproto = nhgets(ghp->eproto);
    184 	QLOCK(gre);
    185 
    186 	/* Look for a conversation structure for this port and address */
    187 	c = nil;
    188 	for(p = gre->conv; *p; p++) {
    189 		c = *p;
    190 		if(c->inuse == 0)
    191 			continue;
    192 		if(c->rport == eproto && 
    193 			(gpriv->raw || ipcmp(c->raddr, raddr) == 0))
    194 			break;
    195 	}
    196 
    197 	if(*p == nil) {
    198 		QUNLOCK(gre);
    199 		freeblist(bp);
    200 		return;
    201 	}
    202 
    203 	QUNLOCK(gre);
    204 
    205 	/*
    206 	 * Trim the packet down to data size
    207 	 */
    208 	len = nhgets(ghp->len) - GRE_IPONLY;
    209 	if(len < GRE_IPPLUSGRE){
    210 		freeblist(bp);
    211 		return;
    212 	}
    213 	bp = trimblock(bp, GRE_IPONLY, len);
    214 	if(bp == nil){
    215 		gpriv->lenerr++;
    216 		return;
    217 	}
    218 
    219 	/*
    220 	 *  Can't delimit packet so pull it all into one block.
    221 	 */
    222 	if(qlen(c->rq) > 64*1024)
    223 		freeblist(bp);
    224 	else{
    225 		bp = concatblock(bp);
    226 		if(bp == 0)
    227 			panic("greiput");
    228 		qpass(c->rq, bp);
    229 	}
    230 }
    231 
    232 int
    233 grestats(Proto *gre, char *buf, int len)
    234 {
    235 	GREpriv *gpriv;
    236 
    237 	gpriv = gre->priv;
    238 
    239 	return snprint(buf, len, "gre: len %lud\n", gpriv->lenerr);
    240 }
    241 
    242 char*
    243 grectl(Conv *c, char **f, int n)
    244 {
    245 	GREpriv *gpriv;
    246 
    247 	gpriv = c->p->priv;
    248 	if(n == 1){
    249 		if(strcmp(f[0], "raw") == 0){
    250 			gpriv->raw = 1;
    251 			return nil;
    252 		}
    253 		else if(strcmp(f[0], "cooked") == 0){
    254 			gpriv->raw = 0;
    255 			return nil;
    256 		}
    257 	}
    258 	return "unknown control request";
    259 }
    260 
    261 void
    262 greinit(Fs *fs)
    263 {
    264 	Proto *gre;
    265 
    266 	gre = smalloc(sizeof(Proto));
    267 	gre->priv = smalloc(sizeof(GREpriv));
    268 	gre->name = "gre";
    269 	gre->connect = greconnect;
    270 	gre->announce = greannounce;
    271 	gre->state = grestate;
    272 	gre->create = grecreate;
    273 	gre->close = greclose;
    274 	gre->rcv = greiput;
    275 	gre->ctl = grectl;
    276 	gre->advise = nil;
    277 	gre->stats = grestats;
    278 	gre->ipproto = IP_GREPROTO;
    279 	gre->nc = 64;
    280 	gre->ptclsize = 0;
    281 
    282 	Fsproto(fs, gre);
    283 }