diff options
Diffstat (limited to 'drivers/net/wan/hdlc_ppp.c')
| -rw-r--r-- | drivers/net/wan/hdlc_ppp.c | 102 |
1 files changed, 54 insertions, 48 deletions
diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c index 0d2e00ece804..159295c4bd6d 100644 --- a/drivers/net/wan/hdlc_ppp.c +++ b/drivers/net/wan/hdlc_ppp.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Generic HDLC support routines for Linux * Point-to-point protocol support * * Copyright (C) 1999 - 2008 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/errno.h> @@ -44,6 +41,7 @@ static const char *const code_names[CP_CODES] = { "0", "ConfReq", "ConfAck", "ConfNak", "ConfRej", "TermReq", "TermAck", "CodeRej", "ProtoRej", "EchoReq", "EchoReply", "Discard" }; + static char debug_buffer[64 + 3 * DEBUG_CP]; #endif @@ -61,7 +59,6 @@ struct cp_header { __be16 len; }; - struct proto { struct net_device *dev; struct timer_list timer; @@ -94,6 +91,7 @@ static const char *const state_names[STATES] = { "Closed", "Stopped", "Stopping", "ReqSent", "AckRecv", "AckSent", "Opened" }; + static const char *const event_names[EVENTS] = { "Start", "Stop", "TO+", "TO-", "RCR+", "RCR-", "RCA", "RCN", "RTR", "RTA", "RUC", "RXJ+", "RXJ-" @@ -102,14 +100,14 @@ static const char *const event_names[EVENTS] = { static struct sk_buff_head tx_queue; /* used when holding the spin lock */ -static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr); +static int ppp_ioctl(struct net_device *dev, struct if_settings *ifs); -static inline struct ppp* get_ppp(struct net_device *dev) +static inline struct ppp *get_ppp(struct net_device *dev) { return (struct ppp *)dev_to_hdlc(dev)->state; } -static inline struct proto* get_proto(struct net_device *dev, u16 pid) +static inline struct proto *get_proto(struct net_device *dev, u16 pid) { struct ppp *ppp = get_ppp(dev); @@ -125,23 +123,21 @@ static inline struct proto* get_proto(struct net_device *dev, u16 pid) } } -static inline const char* proto_name(u16 pid) +static inline const char *proto_name(u16 pid) { switch (pid) { - case PID_LCP: - return "LCP"; case PID_IPCP: return "IPCP"; case PID_IPV6CP: return "IPV6CP"; default: - return NULL; + return "LCP"; } } static __be16 ppp_type_trans(struct sk_buff *skb, struct net_device *dev) { - struct hdlc_header *data = (struct hdlc_header*)skb->data; + struct hdlc_header *data = (struct hdlc_header *)skb->data; if (skb->len < sizeof(struct hdlc_header)) return htons(ETH_P_HDLC); @@ -163,7 +159,6 @@ static __be16 ppp_type_trans(struct sk_buff *skb, struct net_device *dev) } } - static int ppp_hard_header(struct sk_buff *skb, struct net_device *dev, u16 type, const void *daddr, const void *saddr, unsigned int len) @@ -174,7 +169,7 @@ static int ppp_hard_header(struct sk_buff *skb, struct net_device *dev, #endif skb_push(skb, sizeof(struct hdlc_header)); - data = (struct hdlc_header*)skb->data; + data = (struct hdlc_header *)skb->data; data->address = HDLC_ADDR_ALLSTATIONS; data->control = HDLC_CTRL_UI; @@ -196,10 +191,10 @@ static int ppp_hard_header(struct sk_buff *skb, struct net_device *dev, return sizeof(struct hdlc_header); } - static void ppp_tx_flush(void) { struct sk_buff *skb; + while ((skb = skb_dequeue(&tx_queue)) != NULL) dev_queue_xmit(skb); } @@ -222,10 +217,9 @@ static void ppp_tx_cp(struct net_device *dev, u16 pid, u8 code, skb = dev_alloc_skb(sizeof(struct hdlc_header) + sizeof(struct cp_header) + magic_len + len); - if (!skb) { - netdev_warn(dev, "out of memory in ppp_tx_cp()\n"); + if (!skb) return; - } + skb_reserve(skb, sizeof(struct hdlc_header)); cp = skb_put(skb, sizeof(struct cp_header)); @@ -254,11 +248,11 @@ static void ppp_tx_cp(struct net_device *dev, u16 pid, u8 code, skb->priority = TC_PRIO_CONTROL; skb->dev = dev; + skb->protocol = htons(ETH_P_HDLC); skb_reset_network_header(skb); skb_queue_tail(&tx_queue, skb); } - /* State transition table (compare STD-51) Events Actions TO+ = Timeout with counter > 0 irc = Initialize-Restart-Count @@ -296,7 +290,6 @@ static int cp_table[EVENTS][STATES] = { { 0 , 1 , 1 , 1 , 1 , 1 ,IRC|STR|2}, /* RXJ- */ }; - /* SCA: RCR+ must supply id, len and data SCN: RCR- must supply code, id, len and data STA: RTR must supply id @@ -363,7 +356,7 @@ static void ppp_cp_event(struct net_device *dev, u16 pid, u16 event, u8 code, } } if (old_state != CLOSED && proto->state == CLOSED) - del_timer(&proto->timer); + timer_delete(&proto->timer); #if DEBUG_STATE printk(KERN_DEBUG "%s: %s ppp_cp_event(%s) ... %s\n", dev->name, @@ -371,7 +364,6 @@ static void ppp_cp_event(struct net_device *dev, u16 pid, u16 event, u8 code, #endif } - static void ppp_cp_parse_cr(struct net_device *dev, u16 pid, u8 id, unsigned int req_len, const u8 *data) { @@ -380,17 +372,15 @@ static void ppp_cp_parse_cr(struct net_device *dev, u16 pid, u8 id, u8 *out; unsigned int len = req_len, nak_len = 0, rej_len = 0; - if (!(out = kmalloc(len, GFP_ATOMIC))) { + out = kmalloc(len, GFP_ATOMIC); + if (!out) { dev->stats.rx_dropped++; return; /* out of memory, ignore CR packet */ } for (opt = data; len; len -= opt[1], opt += opt[1]) { - if (len < 2 || len < opt[1]) { - dev->stats.rx_errors++; - kfree(out); - return; /* bad packet, drop silently */ - } + if (len < 2 || opt[1] < 2 || len < opt[1]) + goto err_out; if (pid == PID_LCP) switch (opt[0]) { @@ -398,6 +388,8 @@ static void ppp_cp_parse_cr(struct net_device *dev, u16 pid, u8 id, continue; /* MRU always OK and > 1500 bytes? */ case LCP_OPTION_ACCM: /* async control character map */ + if (opt[1] < sizeof(valid_accm)) + goto err_out; if (!memcmp(opt, valid_accm, sizeof(valid_accm))) continue; @@ -409,6 +401,8 @@ static void ppp_cp_parse_cr(struct net_device *dev, u16 pid, u8 id, } break; case LCP_OPTION_MAGIC: + if (len < 6) + goto err_out; if (opt[1] != 6 || (!opt[2] && !opt[3] && !opt[4] && !opt[5])) break; /* reject invalid magic number */ @@ -427,11 +421,16 @@ static void ppp_cp_parse_cr(struct net_device *dev, u16 pid, u8 id, ppp_cp_event(dev, pid, RCR_GOOD, CP_CONF_ACK, id, req_len, data); kfree(out); + return; + +err_out: + dev->stats.rx_errors++; + kfree(out); } static int ppp_rx(struct sk_buff *skb) { - struct hdlc_header *hdr = (struct hdlc_header*)skb->data; + struct hdlc_header *hdr = (struct hdlc_header *)skb->data; struct net_device *dev = skb->dev; struct ppp *ppp = get_ppp(dev); struct proto *proto; @@ -489,7 +488,7 @@ static int ppp_rx(struct sk_buff *skb) if (pid == PID_LCP) switch (cp->code) { case LCP_PROTO_REJ: - pid = ntohs(*(__be16*)skb->data); + pid = ntohs(*(__be16 *)skb->data); if (pid == PID_LCP || pid == PID_IPCP || pid == PID_IPV6CP) ppp_cp_event(dev, pid, RXJ_BAD, 0, 0, @@ -558,13 +557,20 @@ out: return NET_RX_DROP; } -static void ppp_timer(unsigned long arg) +static void ppp_timer(struct timer_list *t) { - struct proto *proto = (struct proto *)arg; + struct proto *proto = timer_container_of(proto, t, timer); struct ppp *ppp = get_ppp(proto->dev); unsigned long flags; spin_lock_irqsave(&ppp->lock, flags); + /* mod_timer could be called after we entered this function but + * before we got the lock. + */ + if (timer_pending(&proto->timer)) { + spin_unlock_irqrestore(&ppp->lock, flags); + return; + } switch (proto->state) { case STOPPING: case REQ_SENT: @@ -574,7 +580,10 @@ static void ppp_timer(unsigned long arg) ppp_cp_event(proto->dev, proto->pid, TO_GOOD, 0, 0, 0, NULL); proto->restart_counter--; - } else + } else if (netif_carrier_ok(proto->dev)) + ppp_cp_event(proto->dev, proto->pid, TO_GOOD, 0, 0, + 0, NULL); + else ppp_cp_event(proto->dev, proto->pid, TO_BAD, 0, 0, 0, NULL); break; @@ -601,7 +610,6 @@ static void ppp_timer(unsigned long arg) ppp_tx_flush(); } - static void ppp_start(struct net_device *dev) { struct ppp *ppp = get_ppp(dev); @@ -609,10 +617,9 @@ static void ppp_start(struct net_device *dev) for (i = 0; i < IDX_COUNT; i++) { struct proto *proto = &ppp->protos[i]; + proto->dev = dev; - init_timer(&proto->timer); - proto->timer.function = ppp_timer; - proto->timer.data = (unsigned long)proto; + timer_setup(&proto->timer, ppp_timer, 0); proto->state = CLOSED; } ppp->protos[IDX_LCP].pid = PID_LCP; @@ -646,17 +653,17 @@ static const struct header_ops ppp_header_ops = { .create = ppp_hard_header, }; -static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr) +static int ppp_ioctl(struct net_device *dev, struct if_settings *ifs) { hdlc_device *hdlc = dev_to_hdlc(dev); struct ppp *ppp; int result; - switch (ifr->ifr_settings.type) { + switch (ifs->type) { case IF_GET_PROTO: if (dev_to_hdlc(dev)->proto != &proto) return -EINVAL; - ifr->ifr_settings.type = IF_PROTO_PPP; + ifs->type = IF_PROTO_PPP; return 0; /* return protocol only, no settable parameters */ case IF_PROTO_PPP: @@ -668,7 +675,8 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr) /* no settable parameters */ - result = hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT); + result = hdlc->attach(dev, ENCODING_NRZ, + PARITY_CRC16_PR1_CCITT); if (result) return result; @@ -695,22 +703,20 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr) return -EINVAL; } - -static int __init mod_init(void) +static int __init hdlc_ppp_init(void) { skb_queue_head_init(&tx_queue); register_hdlc_protocol(&proto); return 0; } -static void __exit mod_exit(void) +static void __exit hdlc_ppp_exit(void) { unregister_hdlc_protocol(&proto); } - -module_init(mod_init); -module_exit(mod_exit); +module_init(hdlc_ppp_init); +module_exit(hdlc_ppp_exit); MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>"); MODULE_DESCRIPTION("PPP protocol support for generic HDLC"); |
