void rtsol_input(int s) { u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ]; int l, ifindex = 0, *hlimp = NULL; ssize_t msglen; struct in6_pktinfo *pi = NULL; struct ifinfo *ifi = NULL; struct ra_opt *rao = NULL; struct icmp6_hdr *icp; struct nd_router_advert *nd_ra; struct cmsghdr *cm; struct rainfo *rai; char *raoptp; char *p; struct in6_addr *addr; struct nd_opt_hdr *ndo; struct nd_opt_rdnss *rdnss; struct nd_opt_dnssl *dnssl; size_t len; char nsbuf[INET6_ADDRSTRLEN + 1 + IFNAMSIZ + 1]; char dname[NI_MAXHOST]; struct timeval now; struct timeval lifetime; int newent_rai; int newent_rao; /* get message. namelen and controllen must always be initialized. */ rcvmhdr.msg_namelen = sizeof(from); rcvmhdr.msg_controllen = rcvcmsglen; if ((msglen = recvmsg(s, &rcvmhdr, 0)) < 0) { warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno)); return; } /* extract optional information via Advanced API */ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); cm; cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) { if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO && cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { pi = (struct in6_pktinfo *)(CMSG_DATA(cm)); ifindex = pi->ipi6_ifindex; } if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_HOPLIMIT && cm->cmsg_len == CMSG_LEN(sizeof(int))) hlimp = (int *)CMSG_DATA(cm); } if (ifindex == 0) { warnmsg(LOG_ERR, __func__, "failed to get receiving interface"); return; } if (hlimp == NULL) { warnmsg(LOG_ERR, __func__, "failed to get receiving hop limit"); return; } if ((size_t)msglen < sizeof(struct nd_router_advert)) { warnmsg(LOG_INFO, __func__, "packet size(%zd) is too short", msglen); return; } icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base; if (icp->icmp6_type != ND_ROUTER_ADVERT) { /* * this should not happen because we configured a filter * that only passes RAs on the receiving socket. */ warnmsg(LOG_ERR, __func__, "invalid icmp type(%d) from %s on %s", icp->icmp6_type, inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if (icp->icmp6_code != 0) { warnmsg(LOG_INFO, __func__, "invalid icmp code(%d) from %s on %s", icp->icmp6_code, inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if (*hlimp != 255) { warnmsg(LOG_INFO, __func__, "invalid RA with hop limit(%d) from %s on %s", *hlimp, inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) { warnmsg(LOG_INFO, __func__, "invalid RA with non link-local source from %s on %s", inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } /* xxx: more validation? */ if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) { warnmsg(LOG_INFO, __func__, "received RA from %s on an unexpected IF(%s)", inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), if_indextoname(pi->ipi6_ifindex, ifnamebuf)); return; } warnmsg(LOG_DEBUG, __func__, "received RA from %s on %s, state is %d", inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf)), ifi->ifname, ifi->state); nd_ra = (struct nd_router_advert *)icp; /* * Process the "O bit." * If the value of OtherConfigFlag changes from FALSE to TRUE, the * host should invoke the stateful autoconfiguration protocol, * requesting information. * [RFC 2462 Section 5.5.3] */ if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_OTHER) && !ifi->otherconfig) { warnmsg(LOG_DEBUG, __func__, "OtherConfigFlag on %s is turned on", ifi->ifname); ifi->otherconfig = 1; CALL_SCRIPT(OTHER, NULL); } gettimeofday(&now, NULL); newent_rai = 0; rai = find_rainfo(ifi, &from); if (rai == NULL) { ELM_MALLOC(rai, exit(1)); rai->rai_ifinfo = ifi; TAILQ_INIT(&rai->rai_ra_opt); rai->rai_saddr.sin6_family = AF_INET6; rai->rai_saddr.sin6_len = sizeof(rai->rai_saddr); memcpy(&rai->rai_saddr.sin6_addr, &from.sin6_addr, sizeof(rai->rai_saddr.sin6_addr)); newent_rai = 1; } #define RA_OPT_NEXT_HDR(x) (struct nd_opt_hdr *)((char *)x + \ (((struct nd_opt_hdr *)x)->nd_opt_len * 8)) /* Process RA options. */ warnmsg(LOG_DEBUG, __func__, "Processing RA"); raoptp = (char *)icp + sizeof(struct nd_router_advert); while (raoptp < (char *)icp + msglen) { ndo = (struct nd_opt_hdr *)raoptp; warnmsg(LOG_DEBUG, __func__, "ndo = %p", raoptp); warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_type = %d", ndo->nd_opt_type); warnmsg(LOG_DEBUG, __func__, "ndo->nd_opt_len = %d", ndo->nd_opt_len); switch (ndo->nd_opt_type) { case ND_OPT_RDNSS: rdnss = (struct nd_opt_rdnss *)raoptp; /* Optlen sanity check (Section 5.3.1 in RFC 6106) */ if (rdnss->nd_opt_rdnss_len < 3) { warnmsg(LOG_INFO, __func__, "too short RDNSS option" "in RA from %s was ignored.", inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf))); break; } addr = (struct in6_addr *)(raoptp + sizeof(*rdnss)); while ((char *)addr < (char *)RA_OPT_NEXT_HDR(raoptp)) { if (inet_ntop(AF_INET6, addr, ntopbuf, sizeof(ntopbuf)) == NULL) { warnmsg(LOG_INFO, __func__, "an invalid address in RDNSS option" " in RA from %s was ignored.", inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf))); addr++; continue; } if (IN6_IS_ADDR_LINKLOCAL(addr)) /* XXX: % has to be escaped here */ l = snprintf(nsbuf, sizeof(nsbuf), "%s%c%s", ntopbuf, SCOPE_DELIMITER, ifi->ifname); else l = snprintf(nsbuf, sizeof(nsbuf), "%s", ntopbuf); if (l < 0 || (size_t)l >= sizeof(nsbuf)) { warnmsg(LOG_ERR, __func__, "address copying error in " "RDNSS option: %d.", l); addr++; continue; } warnmsg(LOG_DEBUG, __func__, "nsbuf = %s", nsbuf); newent_rao = 0; rao = find_raopt(rai, ndo->nd_opt_type, nsbuf, strlen(nsbuf)); if (rao == NULL) { ELM_MALLOC(rao, break); rao->rao_type = ndo->nd_opt_type; rao->rao_len = strlen(nsbuf); rao->rao_msg = strdup(nsbuf); if (rao->rao_msg == NULL) { warnmsg(LOG_ERR, __func__, "strdup failed: %s", strerror(errno)); free(rao); addr++; continue; } newent_rao = 1; } /* Set expiration timer */ memset(&rao->rao_expire, 0, sizeof(rao->rao_expire)); memset(&lifetime, 0, sizeof(lifetime)); lifetime.tv_sec = ntohl(rdnss->nd_opt_rdnss_lifetime); timeradd(&now, &lifetime, &rao->rao_expire); if (newent_rao) TAILQ_INSERT_TAIL(&rai->rai_ra_opt, rao, rao_next); addr++; } break; case ND_OPT_DNSSL: dnssl = (struct nd_opt_dnssl *)raoptp; /* Optlen sanity check (Section 5.3.1 in RFC 6106) */ if (dnssl->nd_opt_dnssl_len < 2) { warnmsg(LOG_INFO, __func__, "too short DNSSL option" "in RA from %s was ignored.", inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, sizeof(ntopbuf))); break; } /* * Ensure NUL-termination in DNSSL in case of * malformed field. */ p = (char *)RA_OPT_NEXT_HDR(raoptp); *(p - 1) = '\0'; p = raoptp + sizeof(*dnssl); while (1 < (len = dname_labeldec(dname, sizeof(dname), p))) { /* length == 1 means empty string */ warnmsg(LOG_DEBUG, __func__, "dname = %s", dname); newent_rao = 0; rao = find_raopt(rai, ndo->nd_opt_type, dname, strlen(dname)); if (rao == NULL) { ELM_MALLOC(rao, break); rao->rao_type = ndo->nd_opt_type; rao->rao_len = strlen(dname); rao->rao_msg = strdup(dname); if (rao->rao_msg == NULL) { warnmsg(LOG_ERR, __func__, "strdup failed: %s", strerror(errno)); free(rao); addr++; continue; } newent_rao = 1; } /* Set expiration timer */ memset(&rao->rao_expire, 0, sizeof(rao->rao_expire)); memset(&lifetime, 0, sizeof(lifetime)); lifetime.tv_sec = ntohl(dnssl->nd_opt_dnssl_lifetime); timeradd(&now, &lifetime, &rao->rao_expire); if (newent_rao) TAILQ_INSERT_TAIL(&rai->rai_ra_opt, rao, rao_next); p += len; } break; default: /* nothing to do for other options */ break; }
static int siop_intr(struct siop_adapter *adp) { struct siop_xfer *siop_xfer = NULL; struct scsi_xfer *xs = NULL; u_long scriptaddr = local_to_PCI((u_long)adp->script); int offset, target, lun, tag, restart = 0, need_reset = 0; uint32_t dsa, irqcode; uint16_t sist; uint8_t dstat, sstat1, istat; istat = readb(adp->addr + SIOP_ISTAT); if ((istat & (ISTAT_INTF | ISTAT_DIP | ISTAT_SIP)) == 0) return 0; if (istat & ISTAT_INTF) { printf("INTRF\n"); writeb(adp->addr + SIOP_ISTAT, ISTAT_INTF); } if ((istat & (ISTAT_DIP | ISTAT_SIP | ISTAT_ABRT)) == (ISTAT_DIP | ISTAT_ABRT)) /* clear abort */ writeb(adp->addr + SIOP_ISTAT, 0); /* use DSA to find the current siop_cmd */ dsa = readl(adp->addr + SIOP_DSA); if (dsa >= local_to_PCI((u_long)adp->xfer) && dsa < local_to_PCI((u_long)adp->xfer) + SIOP_TABLE_SIZE) { dsa -= local_to_PCI((u_long)adp->xfer); siop_xfer = adp->xfer; _inv((u_long)siop_xfer, sizeof(*siop_xfer)); xs = adp->xs; } if (istat & ISTAT_DIP) dstat = readb(adp->addr + SIOP_DSTAT); if (istat & ISTAT_SIP) { if (istat & ISTAT_DIP) delay(10); /* * Can't read sist0 & sist1 independently, or we have to * insert delay */ sist = readw(adp->addr + SIOP_SIST0); sstat1 = readb(adp->addr + SIOP_SSTAT1); if ((sist & SIST0_MA) && need_reset == 0) { if (siop_xfer) { int scratcha0; dstat = readb(adp->addr + SIOP_DSTAT); /* * first restore DSA, in case we were in a S/G * operation. */ writel(adp->addr + SIOP_DSA, local_to_PCI((u_long)siop_xfer)); scratcha0 = readb(adp->addr + SIOP_SCRATCHA); switch (sstat1 & SSTAT1_PHASE_MASK) { case SSTAT1_PHASE_STATUS: /* * previous phase may be aborted for any reason * ( for example, the target has less data to * transfer than requested). Compute resid and * just go to status, the command should * terminate. */ if (scratcha0 & A_flag_data) siop_ma(adp, xs); else if ((dstat & DSTAT_DFE) == 0) siop_clearfifo(adp); CALL_SCRIPT(Ent_status); return 1; case SSTAT1_PHASE_MSGIN: /* * target may be ready to disconnect * Compute resid which would be used later * if a save data pointer is needed. */ if (scratcha0 & A_flag_data) siop_ma(adp, xs); else if ((dstat & DSTAT_DFE) == 0) siop_clearfifo(adp); writeb(adp->addr + SIOP_SCRATCHA, scratcha0 & ~A_flag_data); CALL_SCRIPT(Ent_msgin); return 1; } printf("unexpected phase mismatch %d\n", sstat1 & SSTAT1_PHASE_MASK); } else printf("phase mismatch without command\n"); need_reset = 1; } if (sist & (SIST1_STO << 8)) { /* selection time out, assume there's no device here */ if (siop_xfer) { xs->error = XS_SELTIMEOUT; goto end; } else printf("selection timeout without command\n"); } /* Else it's an unhandled exception (for now). */ printf("unhandled scsi interrupt," " sist=0x%x sstat1=0x%x DSA=0x%x DSP=0x%lx\n", sist, sstat1, dsa, readl(adp->addr + SIOP_DSP) - scriptaddr); if (siop_xfer) { xs->error = XS_SELTIMEOUT; goto end; } need_reset = 1; } if (need_reset) { reset: printf("XXXXX: fatal error, need reset the bus...\n"); return 1; } //scintr: if ((istat & ISTAT_DIP) && (dstat & DSTAT_SIR)) { /* script interrupt */ irqcode = readl(adp->addr + SIOP_DSPS); /* * no command, or an inactive command is only valid for a * reselect interrupt */ if ((irqcode & 0x80) == 0) { if (siop_xfer == NULL) { printf( "script interrupt 0x%x with invalid DSA\n", irqcode); goto reset; } } switch(irqcode) { case A_int_err: printf("error, DSP=0x%lx\n", readl(adp->addr + SIOP_DSP) - scriptaddr); if (xs) { xs->error = XS_SELTIMEOUT; goto end; } else { goto reset; } case A_int_reseltarg: printf("reselect with invalid target\n"); goto reset; case A_int_resellun: target = readb(adp->addr + SIOP_SCRATCHA) & 0xf; lun = readb(adp->addr + SIOP_SCRATCHA + 1); tag = readb(adp->addr + SIOP_SCRATCHA + 2); if (target != adp->xs->target || lun != adp->xs->lun || tag != 0) { printf("unknwon resellun:" " target %d lun %d tag %d\n", target, lun, tag); goto reset; } siop_xfer = adp->xfer; dsa = local_to_PCI((u_long)siop_xfer); writel(adp->addr + SIOP_DSP, dsa + sizeof(struct siop_common_xfer) + Ent_ldsa_reload_dsa); _wbinv((u_long)siop_xfer, sizeof(*siop_xfer)); return 1; case A_int_reseltag: printf("reselect with invalid tag\n"); goto reset; case A_int_disc: offset = readb(adp->addr + SIOP_SCRATCHA + 1); siop_sdp(adp, siop_xfer, xs, offset); #if 0 /* we start again with no offset */ siop_cmd->saved_offset = SIOP_NOOFFSET; #endif _wbinv((u_long)siop_xfer, sizeof(*siop_xfer)); CALL_SCRIPT(Ent_script_sched); return 1; case A_int_resfail: printf("reselect failed\n"); return 1; case A_int_done: if (xs == NULL) { printf("done without command, DSA=0x%lx\n", local_to_PCI((u_long)adp->xfer)); return 1; } /* update resid. */ offset = readb(adp->addr + SIOP_SCRATCHA + 1); #if 0 /* * if we got a disconnect between the last data phase * and the status phase, offset will be 0. In this * case, siop_cmd->saved_offset will have the proper * value if it got updated by the controller */ if (offset == 0 && siop_cmd->saved_offset != SIOP_NOOFFSET) offset = siop_cmd->saved_offset; #endif siop_update_resid(adp, siop_xfer, xs, offset); goto end; default: printf("unknown irqcode %x\n", irqcode); if (xs) { xs->error = XS_SELTIMEOUT; goto end; } goto reset; } return 1; } /* We just should't get there */ panic("siop_intr: I shouldn't be there !"); return 1; end: /* * restart the script now if command completed properly * Otherwise wait for siop_scsicmd_end(), we may need to cleanup the * queue */ xs->status = ctoh32(siop_xfer->siop_tables.status); if (xs->status == SCSI_OK) writel(adp->addr + SIOP_DSP, scriptaddr + Ent_script_sched); else restart = 1; siop_scsicmd_end(adp, xs); if (restart) writel(adp->addr + SIOP_DSP, scriptaddr + Ent_script_sched); return 1; }