📄 ppp.c
字号:
return -EIO; CHECK_PPP(-ENXIO); /* * Ensure that the caller does not wish to send too much. */ if (count > PPP_MTU + PPP_HDRLEN) { if (ppp->flags & SC_DEBUG) printk(KERN_WARNING "ppp_tty_write: truncating user packet " "from %lu to mtu %d\n", (unsigned long) count, PPP_MTU + PPP_HDRLEN); count = PPP_MTU + PPP_HDRLEN; } /* * Allocate a buffer for the data and fetch it from the user space. */ skb = alloc_skb(count, GFP_KERNEL); if (skb == NULL) { printk(KERN_ERR "ppp_tty_write: no memory\n"); return 0; } new_data = skb_put(skb, count); /* * Retrieve the user's buffer */ if (copy_from_user(new_data, data, count)) { kfree_skb(skb); return -EFAULT; } /* * Send the frame */ ppp_send_ctrl(ppp, skb); return (ssize_t) count;}/* * Process the IOCTL call for the tty device. * Only the ioctls that relate to using ppp on async serial lines * are processed here; the rest are handled by ppp_ioctl. */static intppp_tty_ioctl (struct tty_struct *tty, struct file * file, unsigned int param2, unsigned long param3){ struct ppp *ppp = tty2ppp (tty); register int temp_i = 0; int error = -EFAULT; /* * Verify the status of the PPP device. */ if (!ppp || ppp->magic != PPP_MAGIC || !ppp->inuse) return -ENXIO; /* * The user must have an euid of root to do these requests. */ if (!capable(CAP_NET_ADMIN)) return -EPERM; switch (param2) { case PPPIOCGASYNCMAP: /* * Retrieve the transmit async map */ if (put_user(ppp->xmit_async_map[0], (int *) param3)) break; error = 0; break; case PPPIOCSASYNCMAP: /* * Set the transmit async map */ if (get_user(temp_i, (int *) param3)) break; ppp->xmit_async_map[0] = temp_i; if (ppp->flags & SC_DEBUG) printk(KERN_INFO "ppp_tty_ioctl: set xmit asyncmap %x\n", ppp->xmit_async_map[0]); error = 0; break; case PPPIOCSRASYNCMAP: /* * Set the receive async map */ if (get_user(temp_i, (int *) param3)) break; ppp->recv_async_map = temp_i; if (ppp->flags & SC_DEBUG) printk(KERN_INFO "ppp_tty_ioctl: set rcv asyncmap %x\n", ppp->recv_async_map); error = 0; break; case PPPIOCGXASYNCMAP: /* * Get the map of characters to be escaped on transmission. */ if (copy_to_user((void *) param3, ppp->xmit_async_map, sizeof (ppp->xmit_async_map))) break; error = 0; break; case PPPIOCSXASYNCMAP: /* * Set the map of characters to be escaped on transmission. */ { __u32 temp_tbl[8]; if (copy_from_user(temp_tbl, (void *) param3, sizeof (temp_tbl))) break; temp_tbl[1] = 0x00000000; temp_tbl[2] &= ~0x40000000; temp_tbl[3] |= 0x60000000; memcpy(ppp->xmit_async_map, temp_tbl, sizeof (ppp->xmit_async_map)); if (ppp->flags & SC_DEBUG) printk(KERN_INFO "ppp_tty_ioctl: set xasyncmap\n"); error = 0; } break; case PPPIOCXFERUNIT: /* * Set up this PPP unit to be used next time this * process sets a tty to PPP line discipline. */ ppp->backup_tty = tty; ppp->sc_xfer = current->pid; error = 0; break; case TCGETS: case TCGETA: /* * Allow users to read, but not set, the serial port parameters */ error = n_tty_ioctl (tty, file, param2, param3); break; case FIONREAD: /* * Returns how many bytes are available for a read(). */ { unsigned long flags; struct sk_buff *skb; int count = 0; save_flags(flags); cli(); skb = skb_peek(&ppp->rcv_q); if (skb != 0) count = skb->len; restore_flags(flags); if (put_user(count, (int *) param3)) break; error = 0; } break; default: /* * All other ioctl() events will come here. */ error = ppp_ioctl(ppp, param2, param3); break; } return error;}/* * TTY callback. * * Process the poll() statement for the PPP device. */static unsigned intppp_tty_poll(struct tty_struct *tty, struct file *filp, poll_table * wait){ struct ppp *ppp = tty2ppp(tty); unsigned int mask = 0; if (ppp && ppp->magic == PPP_MAGIC && tty == ppp->tty) { CHECK_PPP(0); poll_wait(filp, &ppp->read_wait, wait); if (skb_peek(&ppp->rcv_q) != NULL) mask |= POLLIN | POLLRDNORM; if (tty->flags & (1 << TTY_OTHER_CLOSED) || tty_hung_up_p(filp)) mask |= POLLHUP; mask |= POLLOUT | POLLWRNORM; } return mask;}/* * This function is called by the tty driver when the transmit buffer has * additional space. It is used by the ppp code to continue to transmit * the current buffer should the buffer have been partially sent. */static voidppp_tty_wakeup (struct tty_struct *tty){ struct ppp *ppp = tty2ppp (tty); tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); if (!ppp) return; CHECK_PPP_VOID(); if (tty != ppp->tty) return; if (ppp_tty_push(ppp)) ppp_output_wakeup(ppp);}/* * Send a packet to the peer over an async tty line. * Returns -1 iff the packet could not be accepted at present, * 0 if the packet was accepted but we can't accept another yet, or * 1 if we can accept another packet immediately. * If this procedure returns 0, ppp_output_wakeup will be called * exactly once. */static intppp_async_send(struct ppp *ppp, struct sk_buff *skb){ CHECK_PPP(0); ppp_tty_push(ppp); if (ppp->tpkt != NULL) return -1; ppp->tpkt = skb; ppp->tpkt_pos = 0; return ppp_tty_push(ppp);}/* * Push as much data as possible out to the tty. * Returns 1 if we finished encoding the current frame, 0 otherwise. */static intppp_tty_push(struct ppp *ppp){ int avail, sent, done = 0; struct tty_struct *tty = ppp2tty(ppp); CHECK_PPP(0); if (ppp->tty_pushing) return 0; if (tty == NULL || tty->disc_data != (void *) ppp) goto flush; while (ppp->optr < ppp->olim || ppp->tpkt != 0) { ppp->tty_pushing = 1; avail = ppp->olim - ppp->optr; if (avail > 0) { tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); sent = tty->driver.write(tty, 0, ppp->optr, avail); if (sent < 0) goto flush; /* error, e.g. loss of CD */ ppp->stats.ppp_obytes += sent; ppp->optr += sent; if (sent < avail) { ppp->tty_pushing = 0; return done; } } if (ppp->tpkt != 0) done = ppp_async_encode(ppp); ppp->tty_pushing = 0; } return done;flush: ppp->tty_pushing = 1; ppp->stats.ppp_oerrors++; if (ppp->tpkt != 0) { kfree_skb(ppp->tpkt); ppp->tpkt = 0; done = 1; } ppp->optr = ppp->olim; ppp->tty_pushing = 0; return done;}/* * Procedure to encode the data for async serial transmission. * Does octet stuffing (escaping) and address/control * and protocol compression. * Assumes ppp->opkt != 0 on entry. * Returns 1 if we finished the current frame, 0 otherwise. */static intppp_async_encode(struct ppp *ppp){ int fcs, i, count, c; unsigned char *buf, *buflim; unsigned char *data; int islcp; CHECK_PPP(0); buf = ppp->obuf; ppp->olim = buf; ppp->optr = buf; i = ppp->tpkt_pos; data = ppp->tpkt->data; count = ppp->tpkt->len; fcs = ppp->tfcs; /* * LCP packets with code values between 1 (configure-reqest) * and 7 (code-reject) must be sent as though no options * had been negotiated. */ islcp = PPP_PROTOCOL(data) == PPP_LCP && 1 <= data[PPP_HDRLEN] && data[PPP_HDRLEN] <= 7; if (i == 0) { /* * Start of a new packet - insert the leading FLAG * character if necessary. */ if (islcp || flag_time == 0 || jiffies - ppp->last_xmit >= flag_time) *buf++ = PPP_FLAG; /* only reset idle time for data packets */ if (PPP_PROTOCOL(data) < 0x8000) ppp->last_xmit = jiffies; fcs = PPP_INITFCS; ++ppp->stats.ppp_opackets; ppp->stats.ppp_ooctects += count; /* * Do address/control compression */ if ((ppp->flags & SC_COMP_AC) != 0 && !islcp && PPP_ADDRESS(data) == PPP_ALLSTATIONS && PPP_CONTROL(data) == PPP_UI) i += 2; } /* * Once we put in the last byte, we need to put in the FCS * and closing flag, so make sure there is at least 7 bytes * of free space in the output buffer. */ buflim = buf + OBUFSIZE - 6; while (i < count && buf < buflim) { c = data[i++]; if (i == 3 && c == 0 && (ppp->flags & SC_COMP_PROT)) continue; /* compress protocol field */ fcs = PPP_FCS(fcs, c); if (in_xmap(ppp, c) || (islcp && c < 0x20)) { *buf++ = PPP_ESCAPE; c ^= 0x20; } *buf++ = c; } if (i == count) { /* * We have finished the packet. Add the FCS and flag. */ fcs = ~fcs; c = fcs & 0xff; if (in_xmap(ppp, c) || (islcp && c < 0x20)) { *buf++ = PPP_ESCAPE; c ^= 0x20; } *buf++ = c; c = (fcs >> 8) & 0xff; if (in_xmap(ppp, c) || (islcp && c < 0x20)) { *buf++ = PPP_ESCAPE; c ^= 0x20; } *buf++ = c; *buf++ = PPP_FLAG; ppp->olim = buf; kfree_skb(ppp->tpkt); ppp->tpkt = 0; return 1; } /* * Remember where we are up to in this packet. */ ppp->olim = buf; ppp->tpkt_pos = i; ppp->tfcs = fcs; return 0;}/* * Callback function from tty driver. Return the amount of space left * in the receiver's buffer to decide if remote transmitter is to be * throttled. */static intppp_tty_room (struct tty_struct *tty){ return 65536; /* We can handle an infinite amount of data. :-) */}/* * Callback function when data is available at the tty driver. */static voidppp_tty_receive (struct tty_struct *tty, const __u8 * data, char *flags, int count){ register struct ppp *ppp = tty2ppp (tty); struct sk_buff *skb; int chr, flg; unsigned char *p; if (ppp != 0) CHECK_PPP_VOID(); /* * This can happen if stuff comes in on the backup tty. */ if (ppp == 0 || tty != ppp->tty) return; /* * Verify the table pointer and ensure that the line is * still in PPP discipline. */ if (ppp->magic != PPP_MAGIC) { if (ppp->flags & SC_DEBUG) printk(KERN_DEBUG "PPP: tty_receive called but couldn't find " "PPP struct.\n"); return; } /* * Print the buffer if desired */ if (ppp->flags & SC_LOG_RAWIN) ppp_print_buffer ("receive buffer", data, count); ppp->stats.ppp_ibytes += count; skb = ppp->rpkt; while (count-- > 0) { /* * Collect the character and error condition for the character. * Set the toss flag for the first character error. */ chr = *data++; if (flags) { flg = *flags++; if (flg) { if (ppp->toss == 0) ppp->toss = flg; switch (flg) { case TTY_OVERRUN: ++ppp->estats.rx_fifo_errors; break; case TTY_FRAME: case TTY_BREAK: ++ppp->estats.rx_frame_errors; break; } continue; } } /* * Set the flags for d7 being 0/1 and parity being * even/odd so that the normal processing would have * all flags set at the end of the session. A * missing flag bit indicates an error condition. */#ifdef CHECK_CHARACTERS if (chr & 0x80) ppp->flags |= SC_RCV_B7_1; else ppp->flags |= SC_RCV_B7_0; if (paritytab[chr >> 5] & (1 << (chr & 0x1F))) ppp->flags |= SC_RCV_ODDP; else ppp->flags |= SC_RCV_EVNP;#endif if (chr == PPP_FLAG) { /* * FLAG. This is the end of the block. If the block * ends with ESC FLAG, then the block is to be ignored. */ if (ppp->escape) ppp->toss |= 0x80; /* * Process the frame if it was received correctly. * If there was an error, let the VJ decompressor know. * There are 4 cases here: * skb != NULL, toss != 0: error in frame * skb != NULL, toss == 0: frame ok * skb == NULL, toss != 0: very first frame, * error on 1st char, or alloc_skb failed * skb == NULL, toss == 0: empty frame (~~) */ if (ppp->toss || !ppp_receive_frame(ppp, skb)) { if (ppp->toss && (ppp->flags & SC_DEBUG)) printk(KERN_DEBUG "ppp: tossing frame (%x)\n", ppp->toss); if (skb != NULL) kfree_skb(skb); if (!(ppp->toss == 0xE0 || ppp->toss == 0x80)) ++ppp->stats.ppp_ierrors; ppp_receive_error(ppp); } /* * Reset for the next frame. */ skb = NULL; ppp->rfcs = PPP_INITFCS; ppp->escape = 0; ppp->toss = 0; continue; } /* If we're tossing, look no further. */ if (ppp->toss != 0) continue; /* If this is a control char to be ignored, do so */ if (in_rmap(ppp, chr)) continue; /* * Modify the next character if preceded by escape. * The escape character (0x7d) could be an escaped * 0x5d, if it follows an escape :-) */ if (ppp->escape) { chr ^= PPP_TRANS;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -