static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, const struct flowi *fl) { struct rt6_info *rt = (struct rt6_info*)xdst->route; xdst->u.dst.dev = dev; dev_hold(dev); xdst->u.rt6.rt6i_idev = in6_dev_get(dev); if (!xdst->u.rt6.rt6i_idev) return -ENODEV; xdst->u.rt6.rt6i_peer = rt->rt6i_peer; if (rt->rt6i_peer) atomic_inc(&rt->rt6i_peer->refcnt); xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST | RTF_LOCAL); xdst->u.rt6.rt6i_metric = rt->rt6i_metric; xdst->u.rt6.rt6i_node = rt->rt6i_node; if (rt->rt6i_node) xdst->route_cookie = rt->rt6i_node->fn_sernum; xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway; xdst->u.rt6.rt6i_dst = rt->rt6i_dst; xdst->u.rt6.rt6i_src = rt->rt6i_src; return 0; }
static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev) { struct rt6_info *rt = (struct rt6_info*)xdst->route; xdst->u.dst.dev = dev; dev_hold(dev); xdst->u.rt6.rt6i_idev = in6_dev_get(rt->u.dst.dev); if (!xdst->u.rt6.rt6i_idev) return -ENODEV; /* Sheit... I remember I did this right. Apparently, * it was magically lost, so this code needs audit */ xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST | RTF_LOCAL); xdst->u.rt6.rt6i_metric = rt->rt6i_metric; xdst->u.rt6.rt6i_node = rt->rt6i_node; if (rt->rt6i_node) xdst->route_cookie = rt->rt6i_node->fn_sernum; xdst->u.rt6.rt6i_gateway = rt->rt6i_gateway; xdst->u.rt6.rt6i_dst = rt->rt6i_dst; xdst->u.rt6.rt6i_src = rt->rt6i_src; return 0; }
static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb) { struct inet6_dev *idev = in6_dev_get(skb->dev); if (skb->ip_summed != CHECKSUM_UNNECESSARY) { if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) { UDP6_INC_STATS_BH(UdpInErrors); IP6_INC_STATS_BH(idev,Ip6InDiscards); if (idev) in6_dev_put(idev); kfree_skb(skb); return 0; } skb->ip_summed = CHECKSUM_UNNECESSARY; } if (sock_queue_rcv_skb(sk,skb)<0) { UDP6_INC_STATS_BH(UdpInErrors); IP6_INC_STATS_BH(idev,Ip6InDiscards); if (idev) in6_dev_put(idev); kfree_skb(skb); return 0; } IP6_INC_STATS_BH(idev,Ip6InDelivers); UDP6_INC_STATS_BH(UdpInDatagrams); if (idev) in6_dev_put(idev); return 0; }
/* * check if the interface/address pair is valid */ int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *addr) { struct inet6_dev *idev; struct ifmcaddr6 *mc; #ifdef CONFIG_IPV6_MLD6_DEBUG char abuf[128]; in6_ntop(addr, abuf); MDBG3((KERN_DEBUG "ipv6_chk_mcast_addr(dev=%p(%s), addr=%s)\n", dev, dev->name ? dev->name : "<null>", abuf)); #endif idev = in6_dev_get(dev); if (idev) { read_lock_bh(&idev->lock); for (mc = idev->mc_list; mc; mc=mc->next) { if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0) { read_unlock_bh(&idev->lock); in6_dev_put(idev); return 1; } } read_unlock_bh(&idev->lock); in6_dev_put(idev); } return 0; }
static void rmnet_usb_setup(struct net_device *dev) { #ifdef DISABLE_RMNET_USB_IPV6_ACCEPT_RA_DEFRTR struct inet6_dev *idev = NULL; #endif //DISABLE_RMNET_USB_IPV6_ACCEPT_RA_DEFRTR /* Using Ethernet mode by default */ dev->netdev_ops = &rmnet_usb_ops_ether; /* set this after calling ether_setup */ dev->mtu = RMNET_DATA_LEN; dev->needed_headroom = HEADROOM_FOR_QOS; random_ether_addr(dev->dev_addr); dev->watchdog_timeo = 1000; /* 10 seconds? */ #ifdef DISABLE_RMNET_USB_IPV6_ACCEPT_RA_DEFRTR idev = in6_dev_get(dev); if (idev) { idev->cnf.accept_ra_defrtr = 0; if (idev->dev) pr_info("net_device:%s set accept_ra_defrtr:%d\n", idev->dev->name, idev->cnf.accept_ra_defrtr); } if (idev) in6_dev_put(idev); #endif //DISABLE_RMNET_USB_IPV6_ACCEPT_RA_DEFRTR }
/* * device multicast group del */ int ipv6_dev_mc_dec(struct net_device *dev, struct in6_addr *addr) { struct inet6_dev *idev; struct ifmcaddr6 *ma, **map; idev = in6_dev_get(dev); if (idev == NULL) return -ENODEV; write_lock_bh(&idev->lock); for (map = &idev->mc_list; (ma=*map) != NULL; map = &ma->next) { if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0) { if (--ma->mca_users == 0) { *map = ma->next; write_unlock_bh(&idev->lock); igmp6_group_dropped(ma); ma_put(ma); in6_dev_put(idev); return 0; } write_unlock_bh(&idev->lock); in6_dev_put(idev); return 0; } } write_unlock_bh(&idev->lock); in6_dev_put(idev); return -ENOENT; }
static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, int unregister) { struct xfrm_dst *xdst; if (!unregister) return; xdst = (struct xfrm_dst *)dst; if (xdst->u.rt6.rt6i_idev->dev == dev) { struct inet6_dev *loopback_idev = in6_dev_get(&loopback_dev); BUG_ON(!loopback_idev); do { in6_dev_put(xdst->u.rt6.rt6i_idev); xdst->u.rt6.rt6i_idev = loopback_idev; in6_dev_hold(loopback_idev); xdst = (struct xfrm_dst *)xdst->u.dst.child; } while (xdst->u.dst.xfrm); __in6_dev_put(loopback_idev); } xfrm_dst_ifdown(dst, dev); }
static int igmp6_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data) { off_t pos=0, begin=0; struct ifmcaddr6 *im; int len=0; struct net_device *dev; read_lock(&dev_base_lock); for (dev = dev_base; dev; dev = dev->next) { struct inet6_dev *idev; if ((idev = in6_dev_get(dev)) == NULL) continue; read_lock_bh(&idev->lock); for (im = idev->mc_list; im; im = im->next) { int i; len += sprintf(buffer+len,"%-4d %-15s ", dev->ifindex, dev->name); for (i=0; i<16; i++) len += sprintf(buffer+len, "%02x", im->mca_addr.s6_addr[i]); len+=sprintf(buffer+len, " %5d %08X %ld\n", im->mca_users, im->mca_flags, (im->mca_flags&MAF_TIMER_RUNNING) ? im->mca_timer.expires-jiffies : 0); pos=begin+len; if (pos < offset) { len=0; begin=pos; } if (pos > offset+length) { read_unlock_bh(&idev->lock); in6_dev_put(idev); goto done; } } read_unlock_bh(&idev->lock); in6_dev_put(idev); } *eof = 1; done: read_unlock(&dev_base_lock); *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) len=length; if (len<0) len=0; return len; }
/* * device multicast group inc (add if not found) */ int ipv6_dev_mc_inc(struct net_device *dev, struct in6_addr *addr) { struct ifmcaddr6 *mc; struct inet6_dev *idev; idev = in6_dev_get(dev); if (idev == NULL) return -EINVAL; write_lock_bh(&idev->lock); if (idev->dead) { write_unlock_bh(&idev->lock); in6_dev_put(idev); return -ENODEV; } for (mc = idev->mc_list; mc; mc = mc->next) { if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0) { mc->mca_users++; write_unlock_bh(&idev->lock); in6_dev_put(idev); return 0; } } /* * not found: create a new one. */ mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC); if (mc == NULL) { write_unlock_bh(&idev->lock); in6_dev_put(idev); return -ENOMEM; } memset(mc, 0, sizeof(struct ifmcaddr6)); mc->mca_timer.function = igmp6_timer_handler; mc->mca_timer.data = (unsigned long) mc; memcpy(&mc->mca_addr, addr, sizeof(struct in6_addr)); mc->idev = idev; mc->mca_users = 1; atomic_set(&mc->mca_refcnt, 2); mc->mca_lock = SPIN_LOCK_UNLOCKED; mc->next = idev->mc_list; idev->mc_list = mc; write_unlock_bh(&idev->lock); igmp6_group_added(mc); ma_put(mc); return 0; }
int ip6_pkt_discard(struct sk_buff *skb) { struct inet6_dev *idev = in6_dev_get(skb->dev); IP6_INC_STATS(idev,Ip6OutNoRoutes); icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_NOROUTE, 0, skb->dev); if (idev) in6_dev_put(idev); kfree_skb(skb); return 0; }
static int ipv6_dev_ac_dec(struct net_device *dev, struct in6_addr *addr) { int ret; struct inet6_dev *idev = in6_dev_get(dev); if (idev == NULL) return -ENODEV; ret = __ipv6_dev_ac_dec(idev, addr); in6_dev_put(idev); return ret; }
static int ipv6_get_mtu(struct net_device *dev) { int mtu = IPV6_MIN_MTU; struct inet6_dev *idev; idev = in6_dev_get(dev); if (idev) { mtu = idev->cnf.mtu6; in6_dev_put(idev); } return mtu; }
static int ipv6_get_hoplimit(struct net_device *dev) { int hoplimit = ipv6_devconf.hop_limit; struct inet6_dev *idev; idev = in6_dev_get(dev); if (idev) { hoplimit = idev->cnf.hop_limit; in6_dev_put(idev); } return hoplimit; }
static int vrf_rt6_create(struct net_device *dev) { int flags = DST_HOST | DST_NOPOLICY | DST_NOXFRM | DST_NOCACHE; struct net_vrf *vrf = netdev_priv(dev); struct net *net = dev_net(dev); struct fib6_table *rt6i_table; struct rt6_info *rt6, *rt6_local; int rc = -ENOMEM; /* IPv6 can be CONFIG enabled and then disabled runtime */ if (!ipv6_mod_enabled()) return 0; rt6i_table = fib6_new_table(net, vrf->tb_id); if (!rt6i_table) goto out; /* create a dst for routing packets out a VRF device */ rt6 = ip6_dst_alloc(net, dev, flags); if (!rt6) goto out; dst_hold(&rt6->dst); rt6->rt6i_table = rt6i_table; rt6->dst.output = vrf_output6; /* create a dst for local routing - packets sent locally * to local address via the VRF device as a loopback */ rt6_local = ip6_dst_alloc(net, dev, flags); if (!rt6_local) { dst_release(&rt6->dst); goto out; } dst_hold(&rt6_local->dst); rt6_local->rt6i_idev = in6_dev_get(dev); rt6_local->rt6i_flags = RTF_UP | RTF_NONEXTHOP | RTF_LOCAL; rt6_local->rt6i_table = rt6i_table; rt6_local->dst.input = ip6_input; rcu_assign_pointer(vrf->rt6, rt6); rcu_assign_pointer(vrf->rt6_local, rt6_local); rc = 0; out: return rc; }
static int ndisc_constructor(struct neighbour *neigh) { struct in6_addr *addr = (struct in6_addr*)&neigh->primary_key; struct net_device *dev = neigh->dev; struct inet6_dev *in6_dev; struct neigh_parms *parms; int is_multicast = ipv6_addr_is_multicast(addr); rcu_read_lock(); in6_dev = in6_dev_get(dev); if (in6_dev == NULL) { rcu_read_unlock(); return -EINVAL; } parms = in6_dev->nd_parms; __neigh_parms_put(neigh->parms); neigh->parms = neigh_parms_clone(parms); rcu_read_unlock(); neigh->type = is_multicast ? RTN_MULTICAST : RTN_UNICAST; if (dev->hard_header == NULL) { neigh->nud_state = NUD_NOARP; neigh->ops = &ndisc_direct_ops; neigh->output = neigh->ops->queue_xmit; } else { if (is_multicast) { neigh->nud_state = NUD_NOARP; ndisc_mc_map(addr, neigh->ha, dev, 1); } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) { neigh->nud_state = NUD_NOARP; memcpy(neigh->ha, dev->dev_addr, dev->addr_len); if (dev->flags&IFF_LOOPBACK) neigh->type = RTN_LOCAL; } else if (dev->flags&IFF_POINTOPOINT) { neigh->nud_state = NUD_NOARP; memcpy(neigh->ha, dev->broadcast, dev->addr_len); } if (dev->hard_header_cache) neigh->ops = &ndisc_hh_ops; else neigh->ops = &ndisc_generic_ops; if (neigh->nud_state&NUD_VALID) neigh->output = neigh->ops->connected_output; else neigh->output = neigh->ops->output; } in6_dev_put(in6_dev); return 0; }
/* Some devices are known to send Neigbor Solicitation messages and * require Neigbor Advertisement replies. The IPv6 core will not * respond since IFF_NOARP is set, so we must handle them ourselves. */ static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci) { struct ipv6hdr *iph = (void *)buf; struct nd_msg *msg = (void *)(iph + 1); struct net_device *netdev; struct inet6_dev *in6_dev; bool is_router; /* we'll only respond to requests from unicast addresses to * our solicited node addresses. */ if (!ipv6_addr_is_solict_mult(&iph->daddr) || !(ipv6_addr_type(&iph->saddr) & IPV6_ADDR_UNICAST)) return; /* need to send the NA on the VLAN dev, if any */ rcu_read_lock(); if (tci) { netdev = __vlan_find_dev_deep_rcu(dev->net, htons(ETH_P_8021Q), tci); if (!netdev) { rcu_read_unlock(); return; } } else { netdev = dev->net; } dev_hold(netdev); rcu_read_unlock(); in6_dev = in6_dev_get(netdev); if (!in6_dev) goto out; is_router = !!in6_dev->cnf.forwarding; in6_dev_put(in6_dev); /* ipv6_stub != NULL if in6_dev_get returned an inet6_dev */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0) ipv6_stub->ndisc_send_na(netdev, &iph->saddr, &msg->target, is_router /* router */, true /* solicited */, false /* override */, true /* inc_opt */); #else ipv6_stub->ndisc_send_na(netdev, NULL, &iph->saddr, &msg->target, is_router, true, false, true); #endif out: dev_put(netdev); }
void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, struct neighbour *neigh, const struct in6_addr *daddr, const struct in6_addr *saddr, struct icmp6hdr *icmp6h) { struct flowi fl; struct dst_entry *dst; struct net *net = dev_net(dev); struct sock *sk = net->ipv6.ndisc_sk; struct inet6_dev *idev; int err; u8 type; type = icmp6h->icmp6_type; icmpv6_flow_init(sk, &fl, type, saddr, daddr, dev->ifindex); dst = icmp6_dst_alloc(dev, neigh, daddr); if (!dst) { kfree_skb(skb); return; } err = xfrm_lookup(net, &dst, &fl, NULL, 0); if (err < 0) { kfree_skb(skb); return; } skb_dst_set(skb, dst); idev = in6_dev_get(dst->dev); IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len); err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, dst->dev, dst_output); if (!err) { ICMP6MSGOUT_INC_STATS(net, idev, type); ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); } if (likely(idev != NULL)) in6_dev_put(idev); }
/* * check if the interface has this anycast address */ static int ipv6_chk_acast_dev(struct net_device *dev, struct in6_addr *addr) { struct inet6_dev *idev; struct ifacaddr6 *aca; idev = in6_dev_get(dev); if (idev) { read_lock_bh(&idev->lock); for (aca = idev->ac_list; aca; aca = aca->aca_next) if (ipv6_addr_equal(&aca->aca_addr, addr)) break; read_unlock_bh(&idev->lock); in6_dev_put(idev); return aca != NULL; } return 0; }
/* * Assign bond->master_ipv6 to the next IPv6 address in the list, or * zero it out if there are none. */ static void bond_glean_dev_ipv6(struct net_device *dev, struct in6_addr *addr) { struct inet6_dev *idev; if (!dev) return; idev = in6_dev_get(dev); if (!idev) return; read_lock_bh(&idev->lock); if (!list_empty(&idev->addr_list)) { struct inet6_ifaddr *ifa = list_first_entry(&idev->addr_list, struct inet6_ifaddr, if_list); ipv6_addr_copy(addr, &ifa->addr); } else
static void inc_stats_ipv6(struct sk_buff *skb, int field) { struct inet6_dev *idev; if (is_error(inc_stats_validate(skb))) { /* Maybe we can fall back to increase the stat on the other skb's dev... */ skb = skb_original_skb(skb); if (is_error(inc_stats_validate(skb))) return; } idev = in6_dev_get(skb->dev); if (!idev) return; IP6_INC_STATS_BH(dev_net(skb->dev), idev, field); in6_dev_put(idev); }
int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len) { struct ifmcaddr6 *ma; struct in6_addr *addrp; unsigned long resptime; struct inet6_dev *idev; if (len < sizeof(struct icmp6hdr) + sizeof(struct in6_addr)) return -EINVAL; /* Drop queries with not link local source */ if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL)) return -EINVAL; resptime = ntohs(hdr->icmp6_maxdelay); /* Translate milliseconds to jiffies */ resptime = (resptime<<10)/(1024000/HZ); addrp = (struct in6_addr *) (hdr + 1); idev = in6_dev_get(skb->dev); if (idev == NULL) return 0; read_lock(&idev->lock); if (ipv6_addr_any(addrp)) { for (ma = idev->mc_list; ma; ma=ma->next) igmp6_group_queried(ma, resptime); } else { for (ma = idev->mc_list; ma; ma=ma->next) { if (ipv6_addr_cmp(addrp, &ma->mca_addr) == 0) { igmp6_group_queried(ma, resptime); break; } } } read_unlock(&idev->lock); in6_dev_put(idev); return 0; }
void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, struct neighbour *neigh, struct in6_addr *daddr, struct in6_addr *saddr, struct icmp6hdr *icmp6h) { struct flowi fl; struct dst_entry *dst; struct inet6_dev *idev; int err; u8 type; type = icmp6h->icmp6_type; ndisc_flow_init(&fl, icmp6h->icmp6_type, saddr, daddr, dev->ifindex); dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output); if (!dst) { kfree_skb(skb); return; } err = xfrm_lookup(&dst, &fl, NULL, 0); if (err < 0) { kfree_skb(skb); return; } skb->dst = dst; idev = in6_dev_get(dst->dev); IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS); err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); if (!err) { ICMP6_INC_STATS(idev, type); ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS); } if (likely(idev != NULL)) in6_dev_put(idev); }
int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len) { struct ifmcaddr6 *ma; struct in6_addr *addrp; struct inet6_dev *idev; /* Our own report looped back. Ignore it. */ if (skb->pkt_type == PACKET_LOOPBACK) return 0; if (len < sizeof(struct icmp6hdr) + sizeof(struct in6_addr)) return -EINVAL; /* Drop reports with not link local source */ if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL)) return -EINVAL; addrp = (struct in6_addr *) (hdr + 1); idev = in6_dev_get(skb->dev); if (idev == NULL) return -ENODEV; /* * Cancel the timer for this group */ read_lock(&idev->lock); for (ma = idev->mc_list; ma; ma=ma->next) { if (ipv6_addr_cmp(&ma->mca_addr, addrp) == 0) { spin_lock(&ma->mca_lock); if (del_timer(&ma->mca_timer)) atomic_dec(&ma->mca_refcnt); ma->mca_flags &= ~(MAF_LAST_REPORTER|MAF_TIMER_RUNNING); spin_unlock(&ma->mca_lock); break; } } read_unlock(&idev->lock); in6_dev_put(idev); return 0; }
/* * check if the interface/address pair is valid */ int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *addr) { struct inet6_dev *idev; struct ifmcaddr6 *mc; idev = in6_dev_get(dev); if (idev) { read_lock_bh(&idev->lock); for (mc = idev->mc_list; mc; mc=mc->next) { if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0) { read_unlock_bh(&idev->lock); in6_dev_put(idev); return 1; } } read_unlock_bh(&idev->lock); in6_dev_put(idev); } return 0; }
/* * device multicast group del */ int ipv6_dev_mc_dec(struct net_device *dev, struct in6_addr *addr) { struct inet6_dev *idev; struct ifmcaddr6 *ma, **map; #ifdef CONFIG_IPV6_MLD6_DEBUG char abuf[128]; in6_ntop(addr, abuf); MDBG3((KERN_DEBUG "ipv6_dev_mc_dec(dev=%p(%s), addr=%s)\n", dev, dev->name ? dev->name : "<null>", abuf)); #endif idev = in6_dev_get(dev); if (idev == NULL) return -ENODEV; write_lock_bh(&idev->lock); for (map = &idev->mc_list; (ma=*map) != NULL; map = &ma->next) { if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0) { if (--ma->mca_users == 0) { *map = ma->next; write_unlock_bh(&idev->lock); igmp6_group_dropped(ma); ma_put(ma); in6_dev_put(idev); return 0; } write_unlock_bh(&idev->lock); in6_dev_put(idev); return 0; } } write_unlock_bh(&idev->lock); in6_dev_put(idev); return -ENOENT; }
static void ip6_frag_expire(unsigned long data) { struct frag_queue *fq = (struct frag_queue *) data; struct net_device *dev; struct inet6_dev *idev; spin_lock(&fq->lock); if (fq->last_in & COMPLETE) goto out; fq_kill(fq); dev = dev_get_by_index(fq->iif); idev = dev ? in6_dev_get(dev) : NULL; IP6_INC_STATS_BH(idev,Ip6ReasmTimeout); IP6_INC_STATS_BH(idev,Ip6ReasmFails); /* Send error only if the first segment arrived. */ if (fq->last_in&FIRST_IN && fq->fragments) { /* But use as source device on which LAST ARRIVED segment was received. And do not use fq->dev pointer directly, device might already disappeared. */ if (dev) { fq->fragments->dev = dev; icmpv6_send(fq->fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0, dev); } } if (idev) in6_dev_put(idev); if (dev) dev_put(dev); out: spin_unlock(&fq->lock); fq_put(fq); }
int afinet6_read_devsnmp(char *buffer, char **start, off_t offset, int length, int *eof, void *data) { int len = 0; struct net_device *dev; struct inet6_dev *idev = data; off_t done = 0; if (idev){ done += snmp6_explore(idev->stats.ipv6,snmp6_list_ipv6,offset,length,done,buffer,start); done += snmp6_explore(idev->stats.icmpv6,snmp6_list_icmpv6,offset,length,done,buffer,start); } else{ read_lock(&dev_base_lock); for (dev=dev_base; dev; dev=dev->next) { struct inet6_dev *idev = in6_dev_get(dev); if (!idev) continue; done += snmp6_explore_dev(idev->stats.ipv6,snmp6_list_ipv6,dev->ifindex,offset,length,done,buffer,start); done += snmp6_explore_dev(idev->stats.icmpv6,snmp6_list_icmpv6,dev->ifindex,offset,length,done,buffer,start); in6_dev_put(idev); } read_unlock(&dev_base_lock); } if (done > offset){ len = done - offset; if (len > length) len = length; else if (len < length) *eof = 1; } else{ len = 0; *eof = 1; } return len; }
/* Some devices are known to send Neigbor Solicitation messages and * require Neigbor Advertisement replies. The IPv6 core will not * respond since IFF_NOARP is set, so we must handle them ourselves. */ static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci) { struct ipv6hdr *iph = (void *)buf; struct nd_msg *msg = (void *)(iph + 1); struct net_device *netdev; struct inet6_dev *in6_dev; bool is_router; /* we'll only respond to requests from unicast addresses to * our solicited node addresses. */ if (!ipv6_addr_is_solict_mult(&iph->daddr) || !(ipv6_addr_type(&iph->saddr) & IPV6_ADDR_UNICAST)) return; /* need to send the NA on the VLAN dev, if any */ if (tci) netdev = __vlan_find_dev_deep(dev->net, htons(ETH_P_8021Q), tci); else netdev = dev->net; if (!netdev) return; in6_dev = in6_dev_get(netdev); if (!in6_dev) return; is_router = !!in6_dev->cnf.forwarding; in6_dev_put(in6_dev); /* ipv6_stub != NULL if in6_dev_get returned an inet6_dev */ ipv6_stub->ndisc_send_na(netdev, NULL, &iph->saddr, &msg->target, is_router /* router */, true /* solicited */, false /* override */, true /* inc_opt */); }
static void rmnet_usb_setup(struct net_device *dev, int mux_enabled) { #ifdef DISABLE_RMNET_USB_IPV6_ACCEPT_RA_DEFRTR struct inet6_dev *idev = NULL; #endif dev->netdev_ops = &rmnet_usb_ops_ether; dev->mtu = RMNET_DATA_LEN; if (mux_enabled) { dev->needed_headroom = RMNET_HEADROOM_W_MUX; dev->needed_tailroom = RMNET_TAILROOM; } else { dev->needed_headroom = RMNET_HEADROOM; } random_ether_addr(dev->dev_addr); dev->watchdog_timeo = 1000; #ifdef DISABLE_RMNET_USB_IPV6_ACCEPT_RA_DEFRTR idev = in6_dev_get(dev); if (idev) { idev->cnf.accept_ra_defrtr = 0; if (idev->dev) pr_info("net_device:%s set accept_ra_defrtr:%d\n", idev->dev->name, idev->cnf.accept_ra_defrtr); } if (idev) in6_dev_put(idev); #endif }
void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) { struct sock *sk = igmp6_socket->sk; struct sk_buff *skb; struct inet6_dev *idev; struct icmp6hdr *hdr; struct in6_addr *snd_addr; struct in6_addr *addrp; struct in6_addr addr_buf; struct in6_addr all_routers; int err, len, payload_len, full_len; u8 ra[8] = { IPPROTO_ICMPV6, 0, IPV6_TLV_ROUTERALERT, 2, 0, 0, IPV6_TLV_PADN, 0 }; #ifdef CONFIG_IPV6_MLD6_DEBUG char abuf[128]; in6_ntop(addr, abuf); MDBG3((KERN_DEBUG "igmp6_send(addr=%s, dev=%p(%s), type=%d)\n", abuf, dev, dev->name ? dev->name :"<null>", type)); #endif snd_addr = addr; if (type == ICMPV6_MGM_REDUCTION) { snd_addr = &all_routers; ipv6_addr_all_routers(&all_routers); } len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); payload_len = len + sizeof(ra); full_len = sizeof(struct ipv6hdr) + payload_len; skb = sock_alloc_send_skb(sk, dev->hard_header_len + full_len + 15, 0, &err); if (skb == NULL) return; skb_reserve(skb, (dev->hard_header_len + 15) & ~15); if (dev->hard_header) { unsigned char ha[MAX_ADDR_LEN]; ndisc_mc_map(snd_addr, ha, dev, 1); if (dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, full_len) < 0) goto out; } if (ipv6_get_lladdr(dev, &addr_buf)) { MDBG1((KERN_WARNING "igmp6: %s no linklocal address\n", dev->name)); goto out; } ip6_nd_hdr(sk, skb, dev, &addr_buf, snd_addr, NEXTHDR_HOP, payload_len); memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); hdr = (struct icmp6hdr *) skb_put(skb, sizeof(struct icmp6hdr)); memset(hdr, 0, sizeof(struct icmp6hdr)); hdr->icmp6_type = type; addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr)); ipv6_addr_copy(addrp, addr); hdr->icmp6_cksum = csum_ipv6_magic(&addr_buf, snd_addr, len, IPPROTO_ICMPV6, csum_partial((__u8 *) hdr, len, 0)); dev_queue_xmit(skb); idev = in6_dev_get(dev); if (type == ICMPV6_MGM_REDUCTION) ICMP6_INC_STATS(idev,Icmp6OutGroupMembReductions); else ICMP6_INC_STATS(idev,Icmp6OutGroupMembResponses); ICMP6_INC_STATS(idev,Icmp6OutMsgs); if (idev) in6_dev_put(idev); return; out: kfree_skb(skb); }