static __le16 *dn_mk_ack_header(struct sock *sk, struct sk_buff *skb, unsigned char msgflag, int hlen, int other) { struct dn_scp *scp = DN_SK(sk); unsigned short acknum = scp->numdat_rcv & 0x0FFF; unsigned short ackcrs = scp->numoth_rcv & 0x0FFF; __le16 *ptr; BUG_ON(hlen < 9); scp->ackxmt_dat = acknum; scp->ackxmt_oth = ackcrs; acknum |= 0x8000; ackcrs |= 0x8000; /* If this is an "other data/ack" message, swap acknum and ackcrs */ if (other) { unsigned short tmp = acknum; acknum = ackcrs; ackcrs = tmp; } /* Set "cross subchannel" bit in ackcrs */ ackcrs |= 0x2000; ptr = (__le16 *)dn_mk_common_header(scp, skb, msgflag, hlen); *ptr++ = dn_htons(acknum); *ptr++ = dn_htons(ackcrs); return ptr; }
void dn_send_conn_conf(struct sock *sk, gfp_t gfp) { struct dn_scp *scp = DN_SK(sk); struct sk_buff *skb = NULL; struct nsp_conn_init_msg *msg; __u8 len = (__u8)dn_ntohs(scp->conndata_out.opt_optl); if ((skb = dn_alloc_skb(sk, 50 + len, gfp)) == NULL) return; msg = (struct nsp_conn_init_msg *)skb_put(skb, sizeof(*msg)); msg->msgflg = 0x28; msg->dstaddr = scp->addrrem; msg->srcaddr = scp->addrloc; msg->services = scp->services_loc; msg->info = scp->info_loc; msg->segsize = dn_htons(scp->segsize_loc); *skb_put(skb,1) = len; if (len > 0) memcpy(skb_put(skb, len), scp->conndata_out.opt_data, len); dn_nsp_send(skb); scp->persist = dn_nsp_persist(sk); scp->persist_fxn = dn_nsp_retrans_conn_conf; }
/* * Endnode hello message received */ int dn_neigh_endnode_hello(struct sk_buff *skb) { struct endnode_hello_message *msg = (struct endnode_hello_message *)skb->data; struct neighbour *neigh; struct dn_neigh *dn; dn_address src; src = dn_htons(dn_eth2dn(msg->id)); neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1); dn = (struct dn_neigh *)neigh; if (neigh) { write_lock(&neigh->lock); neigh->used = jiffies; if (!(neigh->nud_state & NUD_PERMANENT)) { neigh->updated = jiffies; if (neigh->dev->type == ARPHRD_ETHER) memcpy(neigh->ha, &skb->mac.ethernet->h_source, ETH_ALEN); dn->flags &= ~(DN_NDFLAG_R1 | DN_NDFLAG_R2); dn->blksize = dn_ntohs(msg->blksize); dn->priority = 0; } write_unlock(&neigh->lock); neigh_release(neigh); } kfree_skb(skb); return 0; }
/* * Ethernet router hello message received */ int dn_neigh_router_hello(struct sk_buff *skb) { struct rtnode_hello_message *msg = (struct rtnode_hello_message *)skb->data; struct neighbour *neigh; struct dn_neigh *dn; struct dn_dev *dn_db; dn_address src; src = dn_htons(dn_eth2dn(msg->id)); neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1); dn = (struct dn_neigh *)neigh; if (neigh) { write_lock(&neigh->lock); neigh->used = jiffies; dn_db = (struct dn_dev *)neigh->dev->dn_ptr; if (!(neigh->nud_state & NUD_PERMANENT)) { neigh->updated = jiffies; if (neigh->dev->type == ARPHRD_ETHER) memcpy(neigh->ha, &skb->mac.ethernet->h_source, ETH_ALEN); dn->blksize = dn_ntohs(msg->blksize); dn->priority = msg->priority; dn->flags &= ~DN_NDFLAG_P3; switch(msg->iinfo & DN_RT_INFO_TYPE) { case DN_RT_INFO_L1RT: dn->flags &=~DN_NDFLAG_R2; dn->flags |= DN_NDFLAG_R1; break; case DN_RT_INFO_L2RT: dn->flags |= DN_NDFLAG_R2; } } if (!dn_db->router) { dn_db->router = neigh_clone(neigh); } else { if (msg->priority > ((struct dn_neigh *)dn_db->router)->priority) neigh_release(xchg(&dn_db->router, neigh_clone(neigh))); } write_unlock(&neigh->lock); neigh_release(neigh); } kfree_skb(skb); return 0; }
/* * Phase 3 output is the same is short output, execpt that * it clears the area bits before transmission. */ static int dn_phase3_output(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; struct neighbour *neigh = dst->neighbour; struct net_device *dev = neigh->dev; int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2; struct dn_short_packet *sp; unsigned char *data; struct dn_skb_cb *cb = DN_SKB_CB(skb); if (skb_headroom(skb) < headroom) { struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom); if (skb2 == NULL) { if (net_ratelimit()) printk(KERN_CRIT "dn_phase3_output: no memory\n"); kfree_skb(skb); return -ENOBUFS; } kfree_skb(skb); skb = skb2; if (net_ratelimit()) printk(KERN_INFO "dn_phase3_output: Increasing headroom\n"); } data = skb_push(skb, sizeof(struct dn_short_packet) + 2); *((unsigned short *)data) = dn_htons(skb->len - 2); sp = (struct dn_short_packet *)(data + 2); sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS)); sp->dstnode = cb->dst & dn_htons(0x03ff); sp->srcnode = cb->src & dn_htons(0x03ff); sp->forward = cb->hops & 0x3f; skb->nh.raw = skb->data; return NF_HOOK(PF_DECnet, NF_DN_POST_ROUTING, skb, NULL, neigh->dev, dn_neigh_output_packet); }
static __le16 *dn_nsp_mk_data_header(struct sock *sk, struct sk_buff *skb, int oth) { struct dn_scp *scp = DN_SK(sk); struct dn_skb_cb *cb = DN_SKB_CB(skb); __le16 *ptr = dn_mk_ack_header(sk, skb, cb->nsp_flags, 11, oth); if (unlikely(oth)) { cb->segnum = scp->numoth; seq_add(&scp->numoth, 1); } else { cb->segnum = scp->numdat; seq_add(&scp->numdat, 1); } *(ptr++) = dn_htons(cb->segnum); return ptr; }
/* * Simple routine to parse an ascii DECnet address * into a network order address. */ static int parse_addr(dn_address *addr, char *str) { dn_address area, node; while(*str && !ISNUM(*str)) str++; if (*str == 0) return -1; area = (*str++ - '0'); if (ISNUM(*str)) { area *= 10; area += (*str++ - '0'); } if (*str++ != '.') return -1; if (!ISNUM(*str)) return -1; node = *str++ - '0'; if (ISNUM(*str)) { node *= 10; node += (*str++ - '0'); } if (ISNUM(*str)) { node *= 10; node += (*str++ - '0'); } if (ISNUM(*str)) { node *= 10; node += (*str++ - '0'); } if ((node > 1023) || (area > 63)) return -1; if (INVALID_END_CHAR(*str)) return -1; *addr = dn_htons((area << 10) | node); return 0; }
static int dn_long_output(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; struct neighbour *neigh = dst->neighbour; struct net_device *dev = neigh->dev; int headroom = dev->hard_header_len + sizeof(struct dn_long_packet) + 3; unsigned char *data; struct dn_long_packet *lp; struct dn_skb_cb *cb = DN_SKB_CB(skb); if (skb_headroom(skb) < headroom) { struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom); if (skb2 == NULL) { if (net_ratelimit()) printk(KERN_CRIT "dn_long_output: no memory\n"); kfree_skb(skb); return -ENOBUFS; } kfree_skb(skb); skb = skb2; if (net_ratelimit()) printk(KERN_INFO "dn_long_output: Increasing headroom\n"); } data = skb_push(skb, sizeof(struct dn_long_packet) + 3); lp = (struct dn_long_packet *)(data+3); *((unsigned short *)data) = dn_htons(skb->len - 2); *(data + 2) = 1 | DN_RT_F_PF; /* Padding */ lp->msgflg = DN_RT_PKT_LONG|(cb->rt_flags&(DN_RT_F_IE|DN_RT_F_RQR|DN_RT_F_RTS)); lp->d_area = lp->d_subarea = 0; dn_dn2eth(lp->d_id, dn_ntohs(cb->dst)); lp->s_area = lp->s_subarea = 0; dn_dn2eth(lp->s_id, dn_ntohs(cb->src)); lp->nl2 = 0; lp->visit_ct = cb->hops & 0x3f; lp->s_class = 0; lp->pt = 0; skb->nh.raw = skb->data; return NF_HOOK(PF_DECnet, NF_DN_POST_ROUTING, skb, NULL, neigh->dev, dn_neigh_output_packet); }
static void dn_nsp_conn_conf(struct sock *sk, struct sk_buff *skb) { struct dn_skb_cb *cb = DN_SKB_CB(skb); struct dn_scp *scp = DN_SK(sk); unsigned char *ptr; if (skb->len < 4) goto out; ptr = skb->data; cb->services = *ptr++; cb->info = *ptr++; cb->segsize = dn_ntohs(*(__le16 *)ptr); if ((scp->state == DN_CI) || (scp->state == DN_CD)) { scp->persist = 0; scp->addrrem = cb->src_port; sk->sk_state = TCP_ESTABLISHED; scp->state = DN_RUN; scp->services_rem = cb->services; scp->info_rem = cb->info; scp->segsize_rem = cb->segsize; if ((scp->services_rem & NSP_FC_MASK) == NSP_FC_NONE) scp->max_window = decnet_no_fc_max_cwnd; if (skb->len > 0) { u16 dlen = *skb->data; if ((dlen <= 16) && (dlen <= skb->len)) { scp->conndata_in.opt_optl = dn_htons(dlen); skb_copy_from_linear_data_offset(skb, 1, scp->conndata_in.opt_data, dlen); } } dn_nsp_send_link(sk, DN_NOCHANGE, 0); if (!sock_flag(sk, SOCK_DEAD)) sk->sk_state_change(sk); } out: kfree_skb(skb); }
static int dnet_pton1(const char *src, struct dn_naddr *dna) { u_int16_t addr; u_int16_t area = 0; u_int16_t node = 0; int pos; pos = dnet_num(src, &area); if ((pos == 0) || (area > 63) || (*(src + pos) != '.')) { return 0; } pos = dnet_num(src + pos + 1, &node); if ((pos == 0) || (node > 1023)) { return 0; } dna->a_len = 2; addr = dn_htons((area << 10) | node); memcpy(dna->a_addr, &addr, sizeof(addr)); return 1; }
static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg, unsigned short reason, gfp_t gfp, struct dst_entry *dst, int ddl, unsigned char *dd, __le16 rem, __le16 loc) { struct sk_buff *skb = NULL; int size = 7 + ddl + ((msgflg == NSP_DISCINIT) ? 1 : 0); unsigned char *msg; if ((dst == NULL) || (rem == 0)) { if (net_ratelimit()) printk(KERN_DEBUG "DECnet: dn_nsp_do_disc: BUG! Please report this to [email protected] rem=%u dst=%p\n", dn_ntohs(rem), dst); return; } if ((skb = dn_alloc_skb(sk, size, gfp)) == NULL) return; msg = skb_put(skb, size); *msg++ = msgflg; *(__le16 *)msg = rem; msg += 2; *(__le16 *)msg = loc; msg += 2; *(__le16 *)msg = dn_htons(reason); msg += 2; if (msgflg == NSP_DISCINIT) *msg++ = ddl; if (ddl) { memcpy(msg, dd, ddl); } /* * This doesn't go via the dn_nsp_send() function since we need * to be able to send disc packets out which have no socket * associations. */ skb->dst = dst_clone(dst); dst_output(skb); }
static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb) { struct dn_scp *scp = DN_SK(sk); struct dn_skb_cb *cb = DN_SKB_CB(skb); unsigned short reason; if (skb->len < 2) goto out; reason = dn_ntohs(*(__le16 *)skb->data); skb_pull(skb, 2); scp->discdata_in.opt_status = dn_htons(reason); scp->discdata_in.opt_optl = 0; memset(scp->discdata_in.opt_data, 0, 16); if (skb->len > 0) { u16 dlen = *skb->data; if ((dlen <= 16) && (dlen <= skb->len)) { scp->discdata_in.opt_optl = dn_htons(dlen); skb_copy_from_linear_data_offset(skb, 1, scp->discdata_in.opt_data, dlen); } } scp->addrrem = cb->src_port; sk->sk_state = TCP_CLOSE; switch(scp->state) { case DN_CI: case DN_CD: scp->state = DN_RJ; sk->sk_err = ECONNREFUSED; break; case DN_RUN: sk->sk_shutdown |= SHUTDOWN_MASK; scp->state = DN_DN; break; case DN_DI: scp->state = DN_DIC; break; } if (!sock_flag(sk, SOCK_DEAD)) { if (sk->sk_socket->state != SS_UNCONNECTED) sk->sk_socket->state = SS_DISCONNECTING; sk->sk_state_change(sk); } /* * It appears that its possible for remote machines to send disc * init messages with no port identifier if we are in the CI and * possibly also the CD state. Obviously we shouldn't reply with * a message if we don't know what the end point is. */ if (scp->addrrem) { dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_ATOMIC); } scp->persist_fxn = dn_destroy_timer; scp->persist = dn_nsp_persist(sk); out: kfree_skb(skb); }
void dn_nsp_send_conninit(struct sock *sk, unsigned char msgflg) { struct dn_scp *scp = DN_SK(sk); struct nsp_conn_init_msg *msg; unsigned char aux; unsigned char menuver; struct dn_skb_cb *cb; unsigned char type = 1; gfp_t allocation = (msgflg == NSP_CI) ? sk->sk_allocation : GFP_ATOMIC; struct sk_buff *skb = dn_alloc_skb(sk, 200, allocation); if (!skb) return; cb = DN_SKB_CB(skb); msg = (struct nsp_conn_init_msg *)skb_put(skb,sizeof(*msg)); msg->msgflg = msgflg; msg->dstaddr = 0x0000; /* Remote Node will assign it*/ msg->srcaddr = scp->addrloc; msg->services = scp->services_loc; /* Requested flow control */ msg->info = scp->info_loc; /* Version Number */ msg->segsize = dn_htons(scp->segsize_loc); /* Max segment size */ if (scp->peer.sdn_objnum) type = 0; skb_put(skb, dn_sockaddr2username(&scp->peer, skb_tail_pointer(skb), type)); skb_put(skb, dn_sockaddr2username(&scp->addr, skb_tail_pointer(skb), 2)); menuver = DN_MENUVER_ACC | DN_MENUVER_USR; if (scp->peer.sdn_flags & SDF_PROXY) menuver |= DN_MENUVER_PRX; if (scp->peer.sdn_flags & SDF_UICPROXY) menuver |= DN_MENUVER_UIC; *skb_put(skb, 1) = menuver; /* Menu Version */ aux = scp->accessdata.acc_userl; *skb_put(skb, 1) = aux; if (aux > 0) memcpy(skb_put(skb, aux), scp->accessdata.acc_user, aux); aux = scp->accessdata.acc_passl; *skb_put(skb, 1) = aux; if (aux > 0) memcpy(skb_put(skb, aux), scp->accessdata.acc_pass, aux); aux = scp->accessdata.acc_accl; *skb_put(skb, 1) = aux; if (aux > 0) memcpy(skb_put(skb, aux), scp->accessdata.acc_acc, aux); aux = (__u8)dn_ntohs(scp->conndata_out.opt_optl); *skb_put(skb, 1) = aux; if (aux > 0) memcpy(skb_put(skb,aux), scp->conndata_out.opt_data, aux); scp->persist = dn_nsp_persist(sk); scp->persist_fxn = dn_nsp_retrans_conninit; cb->rt_flags = DN_RT_F_RQR; dn_nsp_send(skb); }