xref: /plan9-contrib/sys/src/9/ip/gre.c (revision fececb924262ae5acb31c5c448a4a6a523887b15)
1 /*
2  * Generic Routing Encapsulation over IPv4, rfc1702
3  */
4 #include "u.h"
5 #include "../port/lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "../port/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 }
284