This is part 1 of a 3-message set that adds generic PPP support to the generic HDLC layer. These changes were made in PPC Linux 2.4.21-pre4. These 3 parts DO NOT include changes to the PPP daemon to enable it to work with WAN HDLC devices. I shall post those changes to the linux-ppp email list, under the subject "[PATCH] WAN HDLC Support". I welcome comments and suggestions (polite or not). Eventually, this will be used in the real world ;) and I would like it to work properly. ______________________________________________________________________ Part 1 is a new drivers/net/wan/hdlc_ppp.c. I have included it verbatim because I think it is easier to read than a patch (since almost all of it is different from the previous version). Part 2 patches the following: * In include/linux/hdlc.h, the PPP state variables have changed in accordance with the switch from syncppp.c to ppp_generic. * In drivers/net/wan/Makefile, syncppp.c is no longer required when CONFIG_HDLC_PPP is defined. * In include/linux/hdlc/ioctl.h, there is now a structure (ppp_proto) for retrieving PPP protocol information via the SIOCWANDEV(IF_GET_PROTO) ioctl. * In include/linux/if.h, ppp_proto is part of the union used in SIOCWANDEV. Part 3 patches the "sethdlc" program to use the new ppp_proto structure. -- Dan Eble <dane@aiinet.com> _____ . | _ |/| Applied Innovation Inc. | |_| | | http://www.aiinet.com/ |__/|_|_| /* * Generic HDLC support routines for Linux * Point-to-point protocol support * * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl> * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License * as published by the Free Software Foundation. */ #include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/poll.h> #include <linux/errno.h> #include <linux/if_arp.h> #include <linux/init.h> #include <linux/skbuff.h> #include <linux/pkt_sched.h> #include <linux/inetdevice.h> #include <linux/lapb.h> #include <linux/rtnetlink.h> #include <linux/hdlc.h> #include <linux/ppp_defs.h> #include <linux/if_ppp.h> #include <linux/ppp_channel.h> /************************************************************************** * Prototypes *************************************************************************/ static int hdlc_ppp_register(hdlc_device *hdlc); static void hdlc_ppp_unregister(hdlc_device *hdlc); static int hdlc_ppp_dont_change_mtu(struct net_device *dev, int new_mtu); static void hdlc_ppp_netif_rx(struct sk_buff *skb); static int hdlc_genppp_start_xmit(struct ppp_channel *, struct sk_buff *); static int hdlc_genppp_ioctl(struct ppp_channel*, unsigned int, unsigned long); /************************************************************************** * Variables *************************************************************************/ static struct ppp_channel_ops hdlc_genppp_ops = { .start_xmit = hdlc_genppp_start_xmit, .ioctl = hdlc_genppp_ioctl }; /************************************************************************** * Inline Functions *************************************************************************/ /** Get the PPP channel owned by an HDLC device. */ static __inline__ struct ppp_channel* hdlc_to_chan(hdlc_device *hdlc) { return (struct ppp_channel*)&hdlc->state.ppp.chan; } /** Get an HDLC device from its PPP channel. */ static __inline__ hdlc_device* chan_to_hdlc(struct ppp_channel *chan) { return (hdlc_device*)chan->private; } /** Check UP, RUNNING, and carrier all at once. */ static __inline__ int netif_good_to_go(struct net_device *dev) { return (dev->flags & IFF_UP) && netif_running(dev) && netif_carrier_ok(dev); } /************************************************************************** * Functions *************************************************************************/ /** * Initialize and register a PPP channel with the generic PPP layer. * * hdlc_ppp_register() is called from process context while the * interface is down. */ static int hdlc_ppp_register(hdlc_device *hdlc) { struct net_device *const dev = hdlc_to_dev(hdlc); struct ppp_channel *const chan = hdlc_to_chan(hdlc); int old_mtu; int err; /** * Save the old change_mtu and use a new one that prevents the * MTU from being changed while the PPP channel is registered. * Then, call the old function to set an MTU adequate for PPP. */ hdlc->state.ppp.old_change_mtu = dev->change_mtu; dev->change_mtu = hdlc_ppp_dont_change_mtu; old_mtu = dev->mtu; err = hdlc->state.ppp.old_change_mtu(dev, PPP_HDRLEN + PPP_MTU); if (err) { printk(KERN_NOTICE "%s: Changing MTU to %d for PPP failed (%d).\n" "%s: Using current MTU, %d.\n", dev->name, PPP_HDRLEN + PPP_MTU, err, dev->name, dev->mtu); } /* reset the PPP state */ memset(&hdlc->state.ppp, sizeof(hdlc->state.ppp), 0); chan->private = hdlc; chan->ops = &hdlc_genppp_ops; chan->mtu = dev->mtu - PPP_HDRLEN; chan->hdrlen = 2; /* address & control bytes */ /* The ppp_channel object must exist from the time that * ppp_register_channel() is called until after the call to * ppp_unregister_channel() returns. */ err = ppp_register_channel(chan); if (!err) { hdlc->open = NULL; hdlc->stop = NULL; hdlc->proto_detach = hdlc_ppp_unregister; hdlc->netif_rx = hdlc_ppp_netif_rx; hdlc->type_trans = NULL; /* force use of netif_rx() */ hdlc->proto = IF_PROTO_PPP; dev->hard_start_xmit = hdlc->xmit; dev->hard_header = NULL; dev->type = ARPHRD_RAWHDLC; dev->hard_header_len = 0; dev->flags = IFF_POINTOPOINT | IFF_NOARP; dev->addr_len = 0; hdlc->state.ppp.settings.channel = ppp_channel_index(hdlc_to_chan(hdlc)); goto Success; } /* restore old MTU */ hdlc->state.ppp.old_change_mtu(dev, old_mtu); dev->change_mtu = hdlc->state.ppp.old_change_mtu; Success: return err; } /** * Close the channel to the generic PPP layer. * * hdlc_ppp_unregister() is called from process context while the * interface is down. */ static void hdlc_ppp_unregister(hdlc_device *hdlc) { struct net_device *const dev = hdlc_to_dev(hdlc); struct ppp_channel *const chan = hdlc_to_chan(hdlc); /* No thread may be in a call to any of ppp_input(), * ppp_input_error(), ppp_output_wakeup(), ppp_channel_index() * or ppp_unit_number() for a channel at the time that * ppp_unregister_channel() is called for that channel. */ /* By the time a call to ppp_unregister_channel() returns, no * thread will be executing in a call from the generic layer * to that channel's start_xmit() or ioctl() function, and the * generic layer will not call either of those functions * subsequently. */ ppp_unregister_channel(chan); dev->change_mtu = hdlc->state.ppp.old_change_mtu; hdlc->state.ppp.settings.channel = -1; } /** * The channel should initialize the `mtu' and `hdrlen' fields before * calling ppp_register_channel() and not change them until after * ppp_unregister_channel() returns. */ static int hdlc_ppp_dont_change_mtu(struct net_device *dev, int new_mtu) { return -EBUSY; } /** * Receive a buffer from the hardware, strip the PPP header, and pass * the rest to the generic PPP layer. */ static void hdlc_ppp_netif_rx(struct sk_buff *skb) { struct ppp_channel *const chan = hdlc_to_chan(dev_to_hdlc(skb->dev)); unsigned char *p; /* strip address/control field if present */ p = skb->data; if (p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) { /* chop off address/control */ if (skb->len < 3) goto err; p = skb_pull(skb, 2); } /* decompress protocol field if compressed */ if (p[0] & 1) { /* protocol is compressed */ skb_push(skb, 1)[0] = 0; } else if (skb->len < 2) goto err; /* pass to generic layer */ ppp_input(chan, skb); return; err: kfree_skb(skb); ppp_input_error(chan, 0); } /** * Send a packet (or multilink fragment) on this channel. * Returns 1 if it was accepted, 0 to queue it for later. * * The generic layer will not call the start_xmit() function for a * channel while any thread is already executing in that function for * that channel. * * The generic layer may call the channel start_xmit() function at * softirq/BH level but will not call it at interrupt level. Thus the * start_xmit() function may not block. */ static int hdlc_genppp_start_xmit(struct ppp_channel *chan, struct sk_buff *skb) { hdlc_device *const hdlc = chan_to_hdlc(chan); struct net_device *const dev = hdlc_to_dev(hdlc); int proto; unsigned char *data; int islcp; if (!netif_good_to_go(dev)) { /** @todo Instead, return 0 to make generic layer * queue the packet. That will require calling * ppp_output_wakeup() at an appropriate time. */ kfree_skb(skb); ++hdlc->stats.tx_dropped; return 1; } data = skb->data; proto = (data[0] << 8) + data[1]; /* LCP packets with codes between 1 (configure-request) * and 7 (code-reject) must be sent as though no options * have been negotiated. */ islcp = proto == PPP_LCP && 1 <= data[2] && data[2] <= 7; /* compress protocol field if option enabled */ if (data[0] == 0 && (hdlc->state.ppp.flags & SC_COMP_PROT) && !islcp) skb_pull(skb,1); /* prepend address/control fields if necessary */ if ((hdlc->state.ppp.flags & SC_COMP_AC) == 0 || islcp) { if (skb_headroom(skb) < 2) { struct sk_buff *npkt = dev_alloc_skb(skb->len + 2); if (npkt == NULL) { kfree_skb(skb); ++hdlc->stats.tx_dropped; return 1; } skb_reserve(npkt,2); memcpy(skb_put(npkt,skb->len), skb->data, skb->len); kfree_skb(skb); skb = npkt; } skb_push(skb,2); skb->data[0] = PPP_ALLSTATIONS; skb->data[1] = PPP_UI; } skb->dev = dev; skb->nh.raw = skb->data; dev_queue_xmit(skb); return 1; } /** * Handle an ioctl call that has come in via /dev/ppp. * * The generic layer will only call the channel ioctl() function in * process context. * * The generic layer will not call the ioctl() function for a channel * while any thread is already executing in that function for that * channel. */ static int hdlc_genppp_ioctl(struct ppp_channel *chan, unsigned int cmd, unsigned long arg) { hdlc_device *const hdlc = chan_to_hdlc(chan); struct net_device *const dev = hdlc_to_dev(hdlc); int val; int err; err = -EFAULT; switch (cmd) { case PPPIOCGMRU: if (put_user(dev->mtu - PPP_HDRLEN, (int *) arg)) break; err = 0; break; case PPPIOCSMRU: if (get_user(val, (int *) arg)) break; if (val > dev->mtu - PPP_HDRLEN) err = -EINVAL; else err = 0; break; case PPPIOCSFLAGS: if (get_user(val, (int *) arg)) break; val &= SC_MASK; /* keep the bits that are allowed to be set */ hdlc->state.ppp.flags &= ~SC_MASK; hdlc->state.ppp.flags |= val; err = 0; break; default: err = -ENOTTY; } return err; } int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr) { struct net_device *dev = hdlc_to_dev(hdlc); int err; switch (ifr->ifr_settings.type) { case IF_GET_PROTO: ifr->ifr_settings.type = IF_PROTO_PPP; if (ifr->ifr_settings.size < sizeof(ppp_proto)) { ifr->ifr_settings.size = sizeof(ppp_proto); return -ENOBUFS; } if (copy_to_user(ifr->ifr_settings.ifs_ifsu.ppp, &hdlc->state.ppp.settings, sizeof(ppp_proto))) return -EFAULT; return 0; case IF_PROTO_PPP: if(!capable(CAP_NET_ADMIN)) return -EPERM; if(dev->flags & IFF_UP) return -EBUSY; /* no settable parameters */ err = hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); if (!err) { hdlc_proto_detach(hdlc); err = hdlc_ppp_register(hdlc); } return err; } return -EINVAL; } - : send the line "unsubscribe linux-net" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html