// SPDX-License-Identifier: GPL-2.0-only /* * Ethernet netdevice using ATM AAL5 as underlying carrier * (RFC1483 obsoleted by RFC2684) for Linux * * Authors: Marcell GAL, 2000, XDSL Ltd, Hungary * Eric Kinzie, 2006-2007, US Naval Research Laboratory
*/
struct br2684_dev { struct net_device *net_dev; struct list_head br2684_devs; int number; struct list_head brvccs; /* one device <=> one vcc (before xmas) */ int mac_was_set; enum br2684_payload payload;
};
/* * This lock should be held for writing any time the list of devices or * their attached vcc's could be altered. It should be held for reading * any time these are being queried. Note that we sometimes need to * do read-locking under interrupting context, so write locking must block * the current CPU's interrupts.
*/ static DEFINE_RWLOCK(devs_lock);
/* chained vcc->pop function. Check if we should wake the netif_queue */ staticvoid br2684_pop(struct atm_vcc *vcc, struct sk_buff *skb)
{ struct br2684_vcc *brvcc = BR2684_VCC(vcc);
/* If the queue space just went up from zero, wake */ if (atomic_inc_return(&brvcc->qspace) == 1)
netif_wake_queue(brvcc->device);
}
/* * Send a packet out a particular vcc. Not to useful right now, but paves * the way for multiple vcc's per itf. Returns true if we can send, * otherwise false
*/ staticint br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev, struct br2684_vcc *brvcc)
{ struct br2684_dev *brdev = BRPRIV(dev); struct atm_vcc *atmvcc; int minheadroom = (brvcc->encaps == e_llc) ?
((brdev->payload == p_bridged) ? sizeof(llc_oui_pid_pad) : sizeof(llc_oui_ipv4)) :
((brdev->payload == p_bridged) ? BR2684_PAD_LEN : 0);
if (atomic_dec_return(&brvcc->qspace) < 1) { /* No more please! */
netif_stop_queue(brvcc->device); /* We might have raced with br2684_pop() */ if (unlikely(atomic_read(&brvcc->qspace) > 0))
netif_wake_queue(brvcc->device);
}
/* If this fails immediately, the skb will be freed and br2684_pop() will wake the queue if appropriate. Just return an error so that
the stats are updated correctly */ return !atmvcc->send(atmvcc, skb);
}
if (sock_owned_by_user(sk_atm(atmvcc))) {
netif_stop_queue(brvcc->device);
ret = NETDEV_TX_BUSY; goto out;
}
if (!br2684_xmit_vcc(skb, dev, brvcc)) { /* * We should probably use netif_*_queue() here, but that * involves added complication. We need to walk before * we can run. * * Don't free here! this pointer might be no longer valid!
*/
dev->stats.tx_errors++;
dev->stats.tx_fifo_errors++;
}
out:
bh_unlock_sock(sk_atm(atmvcc));
out_devs:
read_unlock(&devs_lock); return ret;
}
/* * We remember when the MAC gets set, so we don't override it later with * the ESI of the ATM card of the first VC
*/ staticint br2684_mac_addr(struct net_device *dev, void *p)
{ int err = eth_mac_addr(dev, p); if (!err)
BRPRIV(dev)->mac_was_set = 1; return err;
}
if (copy_from_user(&fs, arg, sizeof fs)) return -EFAULT; if (fs.ifspec.method != BR2684_FIND_BYNOTHING) { /* * This is really a per-vcc thing, but we can also search * by device.
*/ struct br2684_dev *brdev;
read_lock(&devs_lock);
brdev = BRPRIV(br2684_find_dev(&fs.ifspec)); if (brdev == NULL || list_empty(&brdev->brvccs) ||
brdev->brvccs.next != brdev->brvccs.prev) /* >1 VCC */
brvcc = NULL; else
brvcc = list_entry_brvcc(brdev->brvccs.next);
read_unlock(&devs_lock); if (brvcc == NULL) return -ESRCH;
} else
brvcc = BR2684_VCC(atmvcc);
memcpy(&brvcc->filter, &fs.filter, sizeof(brvcc->filter)); return 0;
}
/* Returns 1 if packet should be dropped */ staticinlineint
packet_fails_filter(__be16 type, struct br2684_vcc *brvcc, struct sk_buff *skb)
{ if (brvcc->filter.netmask == 0) return 0; /* no filter in place */ if (type == htons(ETH_P_IP) &&
(((struct iphdr *)(skb->data))->daddr & brvcc->filter.
netmask) == brvcc->filter.prefix) return 0; if (type == htons(ETH_P_ARP)) return 0; /* * TODO: we should probably filter ARPs too.. don't want to have * them returning values that don't make sense, or is that ok?
*/ return 1; /* drop */
} #endif/* CONFIG_ATM_BR2684_IPFILTER */
staticvoid br2684_close_vcc(struct br2684_vcc *brvcc)
{
pr_debug("removing VCC %p from dev %p\n", brvcc, brvcc->device);
write_lock_irq(&devs_lock);
list_del(&brvcc->brvccs);
write_unlock_irq(&devs_lock);
brvcc->atmvcc->user_back = NULL; /* what about vcc->recvq ??? */
brvcc->atmvcc->release_cb = brvcc->old_release_cb;
brvcc->old_push(brvcc->atmvcc, NULL); /* pass on the bad news */
module_put(brvcc->old_owner);
kfree(brvcc);
}
if (unlikely(skb == NULL)) { /* skb==NULL means VCC is being destroyed */
br2684_close_vcc(brvcc); if (list_empty(&brdev->brvccs)) {
write_lock_irq(&devs_lock);
list_del(&brdev->br2684_devs);
write_unlock_irq(&devs_lock);
unregister_netdev(net_dev);
free_netdev(net_dev);
} return;
}
skb_debug(skb);
atm_return(atmvcc, skb->truesize);
pr_debug("skb from brdev %p\n", brdev); if (brvcc->encaps == e_llc) {
/* accept packets that have "ipv[46]" in the snap header */ if ((skb->len >= (sizeof(llc_oui_ipv4))) &&
(memcmp(skb->data, llc_oui_ipv4, sizeof(llc_oui_ipv4) - BR2684_ETHERTYPE_LEN) == 0)) { if (memcmp(skb->data + 6, ethertype_ipv6, sizeof(ethertype_ipv6)) == 0)
skb->protocol = htons(ETH_P_IPV6); elseif (memcmp(skb->data + 6, ethertype_ipv4, sizeof(ethertype_ipv4)) == 0)
skb->protocol = htons(ETH_P_IP); else goto error;
skb_pull(skb, sizeof(llc_oui_ipv4));
skb_reset_network_header(skb);
skb->pkt_type = PACKET_HOST; /* * Let us waste some time for checking the encapsulation. * Note, that only 7 char is checked so frames with a valid FCS * are also accepted (but FCS is not checked of course).
*/
} elseif ((skb->len >= sizeof(llc_oui_pid_pad)) &&
(memcmp(skb->data, llc_oui_pid_pad, 7) == 0)) {
skb_pull(skb, sizeof(llc_oui_pid_pad));
skb->protocol = eth_type_trans(skb, net_dev);
} else goto error;
/* * Assign a vcc to a dev * Note: we do not have explicit unassign, but look at _push()
*/ staticint br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)
{ struct br2684_vcc *brvcc; struct br2684_dev *brdev; struct net_device *net_dev; struct atm_backend_br2684 be; int err;
if (copy_from_user(&be, arg, sizeof be)) return -EFAULT;
brvcc = kzalloc(sizeof(struct br2684_vcc), GFP_KERNEL); if (!brvcc) return -ENOMEM; /* * Allow two packets in the ATM queue. One actually being sent, and one * for the ATM 'TX done' handler to send. It shouldn't take long to get * the next one from the netdev queue, when we need it. More than that * would be bufferbloat.
*/
atomic_set(&brvcc->qspace, 2);
write_lock_irq(&devs_lock);
net_dev = br2684_find_dev(&be.ifspec); if (net_dev == NULL) {
pr_err("tried to attach to non-existent device\n");
err = -ENXIO; goto error;
}
brdev = BRPRIV(net_dev); if (atmvcc->push == NULL) {
err = -EBADFD; goto error;
} if (!list_empty(&brdev->brvccs)) { /* Only 1 VCC/dev right now */
err = -EEXIST; goto error;
} if (be.fcs_in != BR2684_FCSIN_NO ||
be.fcs_out != BR2684_FCSOUT_NO ||
be.fcs_auto || be.has_vpiid || be.send_padding ||
(be.encaps != BR2684_ENCAPS_VC &&
be.encaps != BR2684_ENCAPS_LLC) ||
be.min_size != 0) {
err = -EINVAL; goto error;
}
pr_debug("vcc=%p, encaps=%d, brvcc=%p\n", atmvcc, be.encaps, brvcc); if (list_empty(&brdev->brvccs) && !brdev->mac_was_set) { unsignedchar *esi = atmvcc->dev->esi; const u8 one = 1;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.