| 1 | /* |
| 2 | * Copyright (c) 1982 Regents of the University of California. |
| 3 | * All rights reserved. The Berkeley software License Agreement |
| 4 | * specifies the terms and conditions for redistribution. |
| 5 | * |
| 6 | * @(#)ns_input.c 6.10 (Berkeley) %G% |
| 7 | */ |
| 8 | |
| 9 | #include "param.h" |
| 10 | #include "systm.h" |
| 11 | #include "mbuf.h" |
| 12 | #include "domain.h" |
| 13 | #include "protosw.h" |
| 14 | #include "socket.h" |
| 15 | #include "socketvar.h" |
| 16 | #include "errno.h" |
| 17 | #include "time.h" |
| 18 | #include "kernel.h" |
| 19 | |
| 20 | #include "../net/if.h" |
| 21 | #include "../net/route.h" |
| 22 | #include "../net/raw_cb.h" |
| 23 | |
| 24 | #include "ns.h" |
| 25 | #include "ns_if.h" |
| 26 | #include "ns_pcb.h" |
| 27 | #include "idp.h" |
| 28 | #include "idp_var.h" |
| 29 | #include "ns_error.h" |
| 30 | |
| 31 | /* |
| 32 | * NS initialization. |
| 33 | */ |
| 34 | union ns_host ns_thishost; |
| 35 | union ns_host ns_zerohost; |
| 36 | union ns_host ns_broadhost; |
| 37 | |
| 38 | static char allones[] = {-1, -1, -1, -1, -1, -1}; |
| 39 | |
| 40 | struct nspcb nspcb; |
| 41 | struct nspcb nsrawpcb; |
| 42 | |
| 43 | struct ifqueue nsintrq; |
| 44 | int nsqmaxlen = IFQ_MAXLEN; |
| 45 | |
| 46 | int idpcksum = 1; |
| 47 | long ns_pexseq; |
| 48 | |
| 49 | ns_init() |
| 50 | { |
| 51 | extern struct timeval time; |
| 52 | |
| 53 | ns_broadhost = * (union ns_host *) allones; |
| 54 | nspcb.nsp_next = nspcb.nsp_prev = &nspcb; |
| 55 | nsrawpcb.nsp_next = nsrawpcb.nsp_prev = &nsrawpcb; |
| 56 | nsintrq.ifq_maxlen = nsqmaxlen; |
| 57 | ns_pexseq = time.tv_usec; |
| 58 | } |
| 59 | |
| 60 | /* |
| 61 | * Idp input routine. Pass to next level. |
| 62 | */ |
| 63 | int nsintr_getpck = 0; |
| 64 | int nsintr_swtch = 0; |
| 65 | nsintr() |
| 66 | { |
| 67 | register struct idp *idp; |
| 68 | register struct mbuf *m; |
| 69 | register struct nspcb *nsp; |
| 70 | struct ifnet *ifp; |
| 71 | struct mbuf *m0; |
| 72 | register int i; |
| 73 | int len, s, error; |
| 74 | char oddpacketp; |
| 75 | |
| 76 | next: |
| 77 | /* |
| 78 | * Get next datagram off input queue and get IDP header |
| 79 | * in first mbuf. |
| 80 | */ |
| 81 | s = splimp(); |
| 82 | IF_DEQUEUEIF(&nsintrq, m, ifp); |
| 83 | splx(s); |
| 84 | nsintr_getpck++; |
| 85 | if (m == 0) |
| 86 | return; |
| 87 | if ((m->m_off > MMAXOFF || m->m_len < sizeof (struct idp)) && |
| 88 | (m = m_pullup(m, sizeof (struct idp))) == 0) { |
| 89 | idpstat.idps_toosmall++; |
| 90 | goto next; |
| 91 | } |
| 92 | |
| 93 | /* |
| 94 | * Give any raw listeners a crack at the packet |
| 95 | */ |
| 96 | for (nsp = nsrawpcb.nsp_next; nsp != &nsrawpcb; nsp = nsp->nsp_next) { |
| 97 | struct mbuf *m1 = m_copy(m, 0, (int)M_COPYALL); |
| 98 | if (m1) idp_input(m1, nsp, ifp); |
| 99 | } |
| 100 | |
| 101 | idp = mtod(m, struct idp *); |
| 102 | len = ntohs(idp->idp_len); |
| 103 | if (oddpacketp = len & 1) { |
| 104 | len++; /* If this packet is of odd length, |
| 105 | preserve garbage byte for checksum */ |
| 106 | } |
| 107 | |
| 108 | /* |
| 109 | * Check that the amount of data in the buffers |
| 110 | * is as at least much as the IDP header would have us expect. |
| 111 | * Trim mbufs if longer than we expect. |
| 112 | * Drop packet if shorter than we expect. |
| 113 | */ |
| 114 | i = -len; |
| 115 | m0 = m; |
| 116 | for (;;) { |
| 117 | i += m->m_len; |
| 118 | if (m->m_next == 0) |
| 119 | break; |
| 120 | m = m->m_next; |
| 121 | } |
| 122 | if (i != 0) { |
| 123 | if (i < 0) { |
| 124 | idpstat.idps_tooshort++; |
| 125 | m = m0; |
| 126 | goto bad; |
| 127 | } |
| 128 | if (i <= m->m_len) |
| 129 | m->m_len -= i; |
| 130 | else |
| 131 | m_adj(m0, -i); |
| 132 | } |
| 133 | m = m0; |
| 134 | if (idpcksum && ((i = idp->idp_sum)!=0xffff)) { |
| 135 | idp->idp_sum = 0; |
| 136 | if (i != (idp->idp_sum = ns_cksum(m,len))) { |
| 137 | idpstat.idps_badsum++; |
| 138 | idp->idp_sum = i; |
| 139 | if (ns_hosteqnh(ns_thishost, idp->idp_dna.x_host)) |
| 140 | error = NS_ERR_BADSUM; |
| 141 | else |
| 142 | error = NS_ERR_BADSUM_T; |
| 143 | ns_error(m, error, 0); |
| 144 | goto next; |
| 145 | } |
| 146 | } |
| 147 | /* |
| 148 | * Is this a directed broadcast? |
| 149 | */ |
| 150 | if (ns_hosteqnh(ns_broadhost,idp->idp_dna.x_host)) { |
| 151 | if ((ns_netof(idp->idp_dna)!=ns_netof(idp->idp_sna)) && |
| 152 | (ns_netof(idp->idp_dna)!=-1) && (ns_netof(idp->idp_sna)!=0) |
| 153 | && (ns_netof(idp->idp_dna)!=0)) { |
| 154 | /* |
| 155 | * Look to see if I need to eat this packet. |
| 156 | * Algorithm is to forward all young packets |
| 157 | * and prematurely age any packets which will |
| 158 | * by physically broadcasted. |
| 159 | * Any very old packets eaten without forwarding |
| 160 | * would die anyway. |
| 161 | * |
| 162 | * Suggestion of Bill Nesheim, Cornell U. |
| 163 | */ |
| 164 | if (idp->idp_tc < NS_MAXHOPS) { |
| 165 | idp_forward(idp); |
| 166 | goto next; |
| 167 | } |
| 168 | } |
| 169 | /* |
| 170 | * Is this our packet? If not, forward. |
| 171 | */ |
| 172 | } else if (!ns_hosteqnh(ns_thishost,idp->idp_dna.x_host)) { |
| 173 | idp_forward(idp); |
| 174 | goto next; |
| 175 | } |
| 176 | /* |
| 177 | * Locate pcb for datagram. |
| 178 | */ |
| 179 | nsp = ns_pcblookup(&idp->idp_sna, idp->idp_dna.x_port, NS_WILDCARD); |
| 180 | /* |
| 181 | * Switch out to protocol's input routine. |
| 182 | */ |
| 183 | nsintr_swtch++; |
| 184 | if (nsp) { |
| 185 | if (oddpacketp) { |
| 186 | m_adj(m0, -1); |
| 187 | } |
| 188 | if ((nsp->nsp_flags & NSP_ALL_PACKETS)==0) |
| 189 | switch (idp->idp_pt) { |
| 190 | |
| 191 | case NSPROTO_SPP: |
| 192 | spp_input(m, nsp, ifp); |
| 193 | goto next; |
| 194 | |
| 195 | case NSPROTO_ERROR: |
| 196 | ns_err_input(m); |
| 197 | goto next; |
| 198 | } |
| 199 | idp_input(m, nsp, ifp); |
| 200 | } else { |
| 201 | ns_error(m, NS_ERR_NOSOCK, 0); |
| 202 | } |
| 203 | goto next; |
| 204 | |
| 205 | bad: |
| 206 | m_freem(m); |
| 207 | goto next; |
| 208 | } |
| 209 | |
| 210 | u_char nsctlerrmap[PRC_NCMDS] = { |
| 211 | ECONNABORTED, ECONNABORTED, 0, 0, |
| 212 | 0, 0, EHOSTDOWN, EHOSTUNREACH, |
| 213 | ENETUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED, |
| 214 | EMSGSIZE, 0, 0, 0, |
| 215 | 0, 0, 0, 0 |
| 216 | }; |
| 217 | |
| 218 | idp_donosocks = 1; |
| 219 | |
| 220 | idp_ctlinput(cmd, arg) |
| 221 | int cmd; |
| 222 | caddr_t arg; |
| 223 | { |
| 224 | struct ns_addr *ns; |
| 225 | struct nspcb *nsp; |
| 226 | struct ns_errp *errp; |
| 227 | int idp_abort(); |
| 228 | extern struct nspcb *idp_drop(); |
| 229 | int type; |
| 230 | |
| 231 | if (cmd < 0 || cmd > PRC_NCMDS) |
| 232 | return; |
| 233 | if (nsctlerrmap[cmd] == 0) |
| 234 | return; /* XXX */ |
| 235 | type = NS_ERR_UNREACH_HOST; |
| 236 | switch (cmd) { |
| 237 | struct sockaddr_ns *sns; |
| 238 | |
| 239 | case PRC_IFDOWN: |
| 240 | case PRC_HOSTDEAD: |
| 241 | case PRC_HOSTUNREACH: |
| 242 | sns = (struct sockaddr_ns *)arg; |
| 243 | if (sns->sns_family != AF_INET) |
| 244 | return; |
| 245 | ns = &sns->sns_addr; |
| 246 | break; |
| 247 | |
| 248 | default: |
| 249 | errp = (struct ns_errp *)arg; |
| 250 | ns = &errp->ns_err_idp.idp_dna; |
| 251 | type = errp->ns_err_num; |
| 252 | type = ntohs((u_short)type); |
| 253 | } |
| 254 | switch (type) { |
| 255 | |
| 256 | case NS_ERR_UNREACH_HOST: |
| 257 | ns_pcbnotify(ns, (int)nsctlerrmap[cmd], idp_abort, (long)0); |
| 258 | break; |
| 259 | |
| 260 | case NS_ERR_NOSOCK: |
| 261 | nsp = ns_pcblookup(ns, errp->ns_err_idp.idp_sna.x_port, |
| 262 | NS_WILDCARD); |
| 263 | if(nsp && idp_donosocks && ! ns_nullhost(nsp->nsp_faddr)) |
| 264 | (void) idp_drop(nsp, (int)nsctlerrmap[cmd]); |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | int idpprintfs = 0; |
| 269 | int idpforwarding = 1; |
| 270 | /* |
| 271 | * Forward a packet. If some error occurs return the sender |
| 272 | * an error packet. Note we can't always generate a meaningful |
| 273 | * error message because the NS errors don't have a large enough repetoire |
| 274 | * of codes and types. |
| 275 | */ |
| 276 | struct route idp_droute; |
| 277 | struct route idp_sroute; |
| 278 | |
| 279 | idp_forward(idp) |
| 280 | register struct idp *idp; |
| 281 | { |
| 282 | register int error, type, code; |
| 283 | struct mbuf *mcopy = NULL; |
| 284 | int agedelta = 1; |
| 285 | int flags = NS_FORWARDING; |
| 286 | int ok_there = 0; |
| 287 | int ok_back = 0; |
| 288 | |
| 289 | if (idpprintfs) { |
| 290 | printf("forward: src "); |
| 291 | ns_printhost(&idp->idp_sna); |
| 292 | printf(", dst "); |
| 293 | ns_printhost(&idp->idp_dna); |
| 294 | printf("hop count %d\n", idp->idp_tc); |
| 295 | } |
| 296 | if (idpforwarding == 0) { |
| 297 | /* can't tell difference between net and host */ |
| 298 | type = NS_ERR_UNREACH_HOST, code = 0; |
| 299 | goto senderror; |
| 300 | } |
| 301 | idp->idp_tc++; |
| 302 | if (idp->idp_tc > NS_MAXHOPS) { |
| 303 | type = NS_ERR_TOO_OLD, code = 0; |
| 304 | goto senderror; |
| 305 | } |
| 306 | /* |
| 307 | * Save at most 42 bytes of the packet in case |
| 308 | * we need to generate an NS error message to the src. |
| 309 | */ |
| 310 | mcopy = m_copy(dtom(idp), 0, imin(ntohs(idp->idp_len), 42)); |
| 311 | |
| 312 | if ((ok_there = idp_do_route(&idp->idp_dna,&idp_droute))==0) { |
| 313 | type = NS_ERR_UNREACH_HOST, code = 0; |
| 314 | goto senderror; |
| 315 | } |
| 316 | /* |
| 317 | * Here we think about forwarding broadcast packets, |
| 318 | * so we try to insure that it doesn't go back out |
| 319 | * on the interface it came in on. Also, if we |
| 320 | * are going to physically broadcast this, let us |
| 321 | * age the packet so we can eat it safely the second time around. |
| 322 | */ |
| 323 | if (idp->idp_dna.x_host.c_host[0] & 0x1) { |
| 324 | struct ns_ifaddr *ia = ns_iaonnetof(idp->idp_dna.x_net); |
| 325 | struct ifnet *ifp; |
| 326 | if (ia) { |
| 327 | /* I'm gonna hafta eat this packet */ |
| 328 | agedelta += NS_MAXHOPS - idp->idp_tc; |
| 329 | idp->idp_tc = NS_MAXHOPS; |
| 330 | } |
| 331 | if ((ok_back = idp_do_route(&idp->idp_sna,&idp_sroute))==0) { |
| 332 | /* error = ENETUNREACH; He'll never get it! */ |
| 333 | m_freem(dtom(idp)); |
| 334 | goto cleanup; |
| 335 | } |
| 336 | if (idp_droute.ro_rt && |
| 337 | (ifp=idp_droute.ro_rt->rt_ifp) && |
| 338 | idp_sroute.ro_rt && |
| 339 | (ifp!=idp_sroute.ro_rt->rt_ifp)) { |
| 340 | flags |= NS_ALLOWBROADCAST; |
| 341 | } else { |
| 342 | type = NS_ERR_UNREACH_HOST, code = 0; |
| 343 | goto senderror; |
| 344 | } |
| 345 | } |
| 346 | /* need to adjust checksum */ |
| 347 | if (idp->idp_sum!=0xffff) { |
| 348 | union bytes { |
| 349 | u_char c[4]; |
| 350 | u_short s[2]; |
| 351 | long l; |
| 352 | } x; |
| 353 | register int shift; |
| 354 | x.l = 0; x.c[0] = agedelta; |
| 355 | shift = (((((int)ntohs(idp->idp_len))+1)>>1)-2) & 0xf; |
| 356 | x.l = idp->idp_sum + (x.l << shift); |
| 357 | x.l = x.s[0] + x.s[1]; |
| 358 | x.l = x.s[0] + x.s[1]; |
| 359 | if (x.l==0xffff) idp->idp_sum = 0; else idp->idp_sum = x.l; |
| 360 | } |
| 361 | if ((error = ns_output(dtom(idp), &idp_droute, flags)) && |
| 362 | (mcopy!=NULL)) { |
| 363 | idp = mtod(mcopy, struct idp *); |
| 364 | type = NS_ERR_UNSPEC_T, code = 0; |
| 365 | switch (error) { |
| 366 | |
| 367 | case ENETUNREACH: |
| 368 | case EHOSTDOWN: |
| 369 | case EHOSTUNREACH: |
| 370 | case ENETDOWN: |
| 371 | case EPERM: |
| 372 | type = NS_ERR_UNREACH_HOST; |
| 373 | break; |
| 374 | |
| 375 | case EMSGSIZE: |
| 376 | type = NS_ERR_TOO_BIG; |
| 377 | code = 576; /* too hard to figure out mtu here */ |
| 378 | break; |
| 379 | |
| 380 | case ENOBUFS: |
| 381 | type = NS_ERR_UNSPEC_T; |
| 382 | break; |
| 383 | } |
| 384 | mcopy = NULL; |
| 385 | senderror: |
| 386 | ns_error(dtom(idp), type, code); |
| 387 | } |
| 388 | cleanup: |
| 389 | if (ok_there) |
| 390 | idp_undo_route(&idp_droute); |
| 391 | if (ok_back) |
| 392 | idp_undo_route(&idp_sroute); |
| 393 | if (mcopy != NULL) |
| 394 | m_freem(mcopy); |
| 395 | } |
| 396 | |
| 397 | idp_do_route(src, ro) |
| 398 | struct ns_addr *src; |
| 399 | struct route *ro; |
| 400 | { |
| 401 | |
| 402 | struct sockaddr_ns *dst; |
| 403 | |
| 404 | bzero((caddr_t)ro, sizeof (*ro)); |
| 405 | dst = (struct sockaddr_ns *)&ro->ro_dst; |
| 406 | |
| 407 | dst->sns_family = AF_NS; |
| 408 | dst->sns_addr = *src; |
| 409 | dst->sns_addr.x_port = 0; |
| 410 | rtalloc(ro); |
| 411 | if (ro->ro_rt == 0 || ro->ro_rt->rt_ifp == 0) { |
| 412 | return (0); |
| 413 | } |
| 414 | ro->ro_rt->rt_use++; |
| 415 | return (1); |
| 416 | } |
| 417 | |
| 418 | idp_undo_route(ro) |
| 419 | register struct route *ro; |
| 420 | { |
| 421 | if (ro->ro_rt) {RTFREE(ro->ro_rt);} |
| 422 | } |
| 423 | ns_watch_output(m) |
| 424 | struct mbuf *m; |
| 425 | { |
| 426 | register struct nspcb *nsp; |
| 427 | /* |
| 428 | * Give any raw listeners a crack at the packet |
| 429 | */ |
| 430 | for (nsp = nsrawpcb.nsp_next; nsp != &nsrawpcb; nsp = nsp->nsp_next) { |
| 431 | struct mbuf *m1 = m_copy(m, 0, (int)M_COPYALL); |
| 432 | if (m1) idp_input(m1, nsp); |
| 433 | } |
| 434 | } |