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 }