181ccbd95SGleb Smirnoff /*- 281ccbd95SGleb Smirnoff * Copyright (c) 2006 Alexander Motin <mav@alkar.net> 381ccbd95SGleb Smirnoff * All rights reserved. 481ccbd95SGleb Smirnoff * 581ccbd95SGleb Smirnoff * Redistribution and use in source and binary forms, with or without 681ccbd95SGleb Smirnoff * modification, are permitted provided that the following conditions 781ccbd95SGleb Smirnoff * are met: 881ccbd95SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright 981ccbd95SGleb Smirnoff * notice unmodified, this list of conditions, and the following 1081ccbd95SGleb Smirnoff * disclaimer. 1181ccbd95SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright 1281ccbd95SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the 1381ccbd95SGleb Smirnoff * documentation and/or other materials provided with the distribution. 1481ccbd95SGleb Smirnoff * 1581ccbd95SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1681ccbd95SGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1781ccbd95SGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1881ccbd95SGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1981ccbd95SGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2081ccbd95SGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2181ccbd95SGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2281ccbd95SGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2381ccbd95SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2481ccbd95SGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2581ccbd95SGleb Smirnoff * SUCH DAMAGE. 2681ccbd95SGleb Smirnoff * 2781ccbd95SGleb Smirnoff * $FreeBSD$ 2881ccbd95SGleb Smirnoff */ 2981ccbd95SGleb Smirnoff 3081ccbd95SGleb Smirnoff /* 3181ccbd95SGleb Smirnoff * Deflate PPP compression netgraph node type. 3281ccbd95SGleb Smirnoff */ 3381ccbd95SGleb Smirnoff 3481ccbd95SGleb Smirnoff #include <sys/param.h> 3581ccbd95SGleb Smirnoff #include <sys/systm.h> 3681ccbd95SGleb Smirnoff #include <sys/kernel.h> 3781ccbd95SGleb Smirnoff #include <sys/mbuf.h> 3881ccbd95SGleb Smirnoff #include <sys/malloc.h> 39b652a5faSAlexander Motin #include <sys/endian.h> 4081ccbd95SGleb Smirnoff #include <sys/errno.h> 4181ccbd95SGleb Smirnoff #include <sys/syslog.h> 4281ccbd95SGleb Smirnoff 4381ccbd95SGleb Smirnoff #include <net/zlib.h> 4481ccbd95SGleb Smirnoff 4581ccbd95SGleb Smirnoff #include <netgraph/ng_message.h> 4681ccbd95SGleb Smirnoff #include <netgraph/netgraph.h> 4781ccbd95SGleb Smirnoff #include <netgraph/ng_parse.h> 4881ccbd95SGleb Smirnoff #include <netgraph/ng_deflate.h> 4981ccbd95SGleb Smirnoff 5081ccbd95SGleb Smirnoff #include "opt_netgraph.h" 5181ccbd95SGleb Smirnoff 52d745c852SEd Schouten static MALLOC_DEFINE(M_NETGRAPH_DEFLATE, "netgraph_deflate", 53d745c852SEd Schouten "netgraph deflate node"); 5481ccbd95SGleb Smirnoff 5581ccbd95SGleb Smirnoff /* DEFLATE header length */ 5681ccbd95SGleb Smirnoff #define DEFLATE_HDRLEN 2 5781ccbd95SGleb Smirnoff 5881ccbd95SGleb Smirnoff #define PROT_COMPD 0x00fd 5981ccbd95SGleb Smirnoff 6081ccbd95SGleb Smirnoff #define DEFLATE_BUF_SIZE 4096 6181ccbd95SGleb Smirnoff 6281ccbd95SGleb Smirnoff /* Node private data */ 6381ccbd95SGleb Smirnoff struct ng_deflate_private { 6481ccbd95SGleb Smirnoff struct ng_deflate_config cfg; /* configuration */ 6581ccbd95SGleb Smirnoff u_char inbuf[DEFLATE_BUF_SIZE]; /* input buffer */ 6681ccbd95SGleb Smirnoff u_char outbuf[DEFLATE_BUF_SIZE]; /* output buffer */ 6781ccbd95SGleb Smirnoff z_stream cx; /* compression context */ 6881ccbd95SGleb Smirnoff struct ng_deflate_stats stats; /* statistics */ 6981ccbd95SGleb Smirnoff ng_ID_t ctrlnode; /* path to controlling node */ 7081ccbd95SGleb Smirnoff uint16_t seqnum; /* sequence number */ 7181ccbd95SGleb Smirnoff u_char compress; /* compress/decompress flag */ 7281ccbd95SGleb Smirnoff }; 7381ccbd95SGleb Smirnoff typedef struct ng_deflate_private *priv_p; 7481ccbd95SGleb Smirnoff 7581ccbd95SGleb Smirnoff /* Netgraph node methods */ 7681ccbd95SGleb Smirnoff static ng_constructor_t ng_deflate_constructor; 7781ccbd95SGleb Smirnoff static ng_rcvmsg_t ng_deflate_rcvmsg; 7881ccbd95SGleb Smirnoff static ng_shutdown_t ng_deflate_shutdown; 7981ccbd95SGleb Smirnoff static ng_newhook_t ng_deflate_newhook; 8081ccbd95SGleb Smirnoff static ng_rcvdata_t ng_deflate_rcvdata; 8181ccbd95SGleb Smirnoff static ng_disconnect_t ng_deflate_disconnect; 8281ccbd95SGleb Smirnoff 8381ccbd95SGleb Smirnoff /* Helper functions */ 8481ccbd95SGleb Smirnoff static void *z_alloc(void *, u_int items, u_int size); 8581ccbd95SGleb Smirnoff static void z_free(void *, void *ptr); 8681ccbd95SGleb Smirnoff static int ng_deflate_compress(node_p node, 8781ccbd95SGleb Smirnoff struct mbuf *m, struct mbuf **resultp); 8881ccbd95SGleb Smirnoff static int ng_deflate_decompress(node_p node, 8981ccbd95SGleb Smirnoff struct mbuf *m, struct mbuf **resultp); 9081ccbd95SGleb Smirnoff static void ng_deflate_reset_req(node_p node); 9181ccbd95SGleb Smirnoff 9281ccbd95SGleb Smirnoff /* Parse type for struct ng_deflate_config. */ 9381ccbd95SGleb Smirnoff static const struct ng_parse_struct_field ng_deflate_config_type_fields[] 9481ccbd95SGleb Smirnoff = NG_DEFLATE_CONFIG_INFO; 9581ccbd95SGleb Smirnoff static const struct ng_parse_type ng_deflate_config_type = { 9681ccbd95SGleb Smirnoff &ng_parse_struct_type, 9781ccbd95SGleb Smirnoff ng_deflate_config_type_fields 9881ccbd95SGleb Smirnoff }; 9981ccbd95SGleb Smirnoff 10081ccbd95SGleb Smirnoff /* Parse type for struct ng_deflate_stat. */ 10181ccbd95SGleb Smirnoff static const struct ng_parse_struct_field ng_deflate_stats_type_fields[] 10281ccbd95SGleb Smirnoff = NG_DEFLATE_STATS_INFO; 10381ccbd95SGleb Smirnoff static const struct ng_parse_type ng_deflate_stat_type = { 10481ccbd95SGleb Smirnoff &ng_parse_struct_type, 10581ccbd95SGleb Smirnoff ng_deflate_stats_type_fields 10681ccbd95SGleb Smirnoff }; 10781ccbd95SGleb Smirnoff 10881ccbd95SGleb Smirnoff /* List of commands and how to convert arguments to/from ASCII. */ 10981ccbd95SGleb Smirnoff static const struct ng_cmdlist ng_deflate_cmds[] = { 11081ccbd95SGleb Smirnoff { 11181ccbd95SGleb Smirnoff NGM_DEFLATE_COOKIE, 11281ccbd95SGleb Smirnoff NGM_DEFLATE_CONFIG, 11381ccbd95SGleb Smirnoff "config", 11481ccbd95SGleb Smirnoff &ng_deflate_config_type, 11581ccbd95SGleb Smirnoff NULL 11681ccbd95SGleb Smirnoff }, 11781ccbd95SGleb Smirnoff { 11881ccbd95SGleb Smirnoff NGM_DEFLATE_COOKIE, 11981ccbd95SGleb Smirnoff NGM_DEFLATE_RESETREQ, 12081ccbd95SGleb Smirnoff "resetreq", 12181ccbd95SGleb Smirnoff NULL, 12281ccbd95SGleb Smirnoff NULL 12381ccbd95SGleb Smirnoff }, 12481ccbd95SGleb Smirnoff { 12581ccbd95SGleb Smirnoff NGM_DEFLATE_COOKIE, 12681ccbd95SGleb Smirnoff NGM_DEFLATE_GET_STATS, 12781ccbd95SGleb Smirnoff "getstats", 12881ccbd95SGleb Smirnoff NULL, 12981ccbd95SGleb Smirnoff &ng_deflate_stat_type 13081ccbd95SGleb Smirnoff }, 13181ccbd95SGleb Smirnoff { 13281ccbd95SGleb Smirnoff NGM_DEFLATE_COOKIE, 13381ccbd95SGleb Smirnoff NGM_DEFLATE_CLR_STATS, 13481ccbd95SGleb Smirnoff "clrstats", 13581ccbd95SGleb Smirnoff NULL, 13681ccbd95SGleb Smirnoff NULL 13781ccbd95SGleb Smirnoff }, 13881ccbd95SGleb Smirnoff { 13981ccbd95SGleb Smirnoff NGM_DEFLATE_COOKIE, 14081ccbd95SGleb Smirnoff NGM_DEFLATE_GETCLR_STATS, 14181ccbd95SGleb Smirnoff "getclrstats", 14281ccbd95SGleb Smirnoff NULL, 14381ccbd95SGleb Smirnoff &ng_deflate_stat_type 14481ccbd95SGleb Smirnoff }, 14581ccbd95SGleb Smirnoff { 0 } 14681ccbd95SGleb Smirnoff }; 14781ccbd95SGleb Smirnoff 14881ccbd95SGleb Smirnoff /* Node type descriptor */ 14981ccbd95SGleb Smirnoff static struct ng_type ng_deflate_typestruct = { 15081ccbd95SGleb Smirnoff .version = NG_ABI_VERSION, 15181ccbd95SGleb Smirnoff .name = NG_DEFLATE_NODE_TYPE, 15281ccbd95SGleb Smirnoff .constructor = ng_deflate_constructor, 15381ccbd95SGleb Smirnoff .rcvmsg = ng_deflate_rcvmsg, 15481ccbd95SGleb Smirnoff .shutdown = ng_deflate_shutdown, 15581ccbd95SGleb Smirnoff .newhook = ng_deflate_newhook, 15681ccbd95SGleb Smirnoff .rcvdata = ng_deflate_rcvdata, 15781ccbd95SGleb Smirnoff .disconnect = ng_deflate_disconnect, 15881ccbd95SGleb Smirnoff .cmdlist = ng_deflate_cmds, 15981ccbd95SGleb Smirnoff }; 16081ccbd95SGleb Smirnoff NETGRAPH_INIT(deflate, &ng_deflate_typestruct); 16181ccbd95SGleb Smirnoff 16281ccbd95SGleb Smirnoff /* Depend on separate zlib module. */ 16381ccbd95SGleb Smirnoff MODULE_DEPEND(ng_deflate, zlib, 1, 1, 1); 16481ccbd95SGleb Smirnoff 16581ccbd95SGleb Smirnoff #define ERROUT(x) do { error = (x); goto done; } while (0) 16681ccbd95SGleb Smirnoff 16781ccbd95SGleb Smirnoff /************************************************************************ 16881ccbd95SGleb Smirnoff NETGRAPH NODE STUFF 16981ccbd95SGleb Smirnoff ************************************************************************/ 17081ccbd95SGleb Smirnoff 17181ccbd95SGleb Smirnoff /* 17281ccbd95SGleb Smirnoff * Node type constructor 17381ccbd95SGleb Smirnoff */ 17481ccbd95SGleb Smirnoff static int 17581ccbd95SGleb Smirnoff ng_deflate_constructor(node_p node) 17681ccbd95SGleb Smirnoff { 17781ccbd95SGleb Smirnoff priv_p priv; 17881ccbd95SGleb Smirnoff 17981ccbd95SGleb Smirnoff /* Allocate private structure. */ 18081ccbd95SGleb Smirnoff priv = malloc(sizeof(*priv), M_NETGRAPH_DEFLATE, M_WAITOK | M_ZERO); 18181ccbd95SGleb Smirnoff 18281ccbd95SGleb Smirnoff NG_NODE_SET_PRIVATE(node, priv); 18381ccbd95SGleb Smirnoff 18481ccbd95SGleb Smirnoff /* This node is not thread safe. */ 18581ccbd95SGleb Smirnoff NG_NODE_FORCE_WRITER(node); 18681ccbd95SGleb Smirnoff 18781ccbd95SGleb Smirnoff /* Done */ 18881ccbd95SGleb Smirnoff return (0); 18981ccbd95SGleb Smirnoff } 19081ccbd95SGleb Smirnoff 19181ccbd95SGleb Smirnoff /* 19281ccbd95SGleb Smirnoff * Give our OK for a hook to be added. 19381ccbd95SGleb Smirnoff */ 19481ccbd95SGleb Smirnoff static int 19581ccbd95SGleb Smirnoff ng_deflate_newhook(node_p node, hook_p hook, const char *name) 19681ccbd95SGleb Smirnoff { 19781ccbd95SGleb Smirnoff const priv_p priv = NG_NODE_PRIVATE(node); 19881ccbd95SGleb Smirnoff 19981ccbd95SGleb Smirnoff if (NG_NODE_NUMHOOKS(node) > 0) 20081ccbd95SGleb Smirnoff return (EINVAL); 20181ccbd95SGleb Smirnoff 20281ccbd95SGleb Smirnoff if (strcmp(name, NG_DEFLATE_HOOK_COMP) == 0) 20381ccbd95SGleb Smirnoff priv->compress = 1; 20481ccbd95SGleb Smirnoff else if (strcmp(name, NG_DEFLATE_HOOK_DECOMP) == 0) 20581ccbd95SGleb Smirnoff priv->compress = 0; 20681ccbd95SGleb Smirnoff else 20781ccbd95SGleb Smirnoff return (EINVAL); 20881ccbd95SGleb Smirnoff 20981ccbd95SGleb Smirnoff return (0); 21081ccbd95SGleb Smirnoff } 21181ccbd95SGleb Smirnoff 21281ccbd95SGleb Smirnoff /* 21381ccbd95SGleb Smirnoff * Receive a control message 21481ccbd95SGleb Smirnoff */ 21581ccbd95SGleb Smirnoff static int 21681ccbd95SGleb Smirnoff ng_deflate_rcvmsg(node_p node, item_p item, hook_p lasthook) 21781ccbd95SGleb Smirnoff { 21881ccbd95SGleb Smirnoff const priv_p priv = NG_NODE_PRIVATE(node); 21981ccbd95SGleb Smirnoff struct ng_mesg *resp = NULL; 22081ccbd95SGleb Smirnoff int error = 0; 22181ccbd95SGleb Smirnoff struct ng_mesg *msg; 22281ccbd95SGleb Smirnoff 22381ccbd95SGleb Smirnoff NGI_GET_MSG(item, msg); 22481ccbd95SGleb Smirnoff 22581ccbd95SGleb Smirnoff if (msg->header.typecookie != NGM_DEFLATE_COOKIE) 22681ccbd95SGleb Smirnoff ERROUT(EINVAL); 22781ccbd95SGleb Smirnoff 22881ccbd95SGleb Smirnoff switch (msg->header.cmd) { 22981ccbd95SGleb Smirnoff case NGM_DEFLATE_CONFIG: 23081ccbd95SGleb Smirnoff { 23181ccbd95SGleb Smirnoff struct ng_deflate_config *const cfg 23281ccbd95SGleb Smirnoff = (struct ng_deflate_config *)msg->data; 23381ccbd95SGleb Smirnoff 23481ccbd95SGleb Smirnoff /* Check configuration. */ 23581ccbd95SGleb Smirnoff if (msg->header.arglen != sizeof(*cfg)) 23681ccbd95SGleb Smirnoff ERROUT(EINVAL); 23781ccbd95SGleb Smirnoff if (cfg->enable) { 23881ccbd95SGleb Smirnoff if (cfg->windowBits < 8 || cfg->windowBits > 15) 23981ccbd95SGleb Smirnoff ERROUT(EINVAL); 24081ccbd95SGleb Smirnoff } else 24181ccbd95SGleb Smirnoff cfg->windowBits = 0; 24281ccbd95SGleb Smirnoff 24381ccbd95SGleb Smirnoff /* Clear previous state. */ 24481ccbd95SGleb Smirnoff if (priv->cfg.enable) { 24581ccbd95SGleb Smirnoff if (priv->compress) 24681ccbd95SGleb Smirnoff deflateEnd(&priv->cx); 24781ccbd95SGleb Smirnoff else 24881ccbd95SGleb Smirnoff inflateEnd(&priv->cx); 24981ccbd95SGleb Smirnoff priv->cfg.enable = 0; 25081ccbd95SGleb Smirnoff } 25181ccbd95SGleb Smirnoff 25281ccbd95SGleb Smirnoff /* Configuration is OK, reset to it. */ 25381ccbd95SGleb Smirnoff priv->cfg = *cfg; 25481ccbd95SGleb Smirnoff 25581ccbd95SGleb Smirnoff if (priv->cfg.enable) { 25681ccbd95SGleb Smirnoff priv->cx.next_in = NULL; 25781ccbd95SGleb Smirnoff priv->cx.zalloc = z_alloc; 25881ccbd95SGleb Smirnoff priv->cx.zfree = z_free; 25981ccbd95SGleb Smirnoff int res; 26081ccbd95SGleb Smirnoff if (priv->compress) { 26181ccbd95SGleb Smirnoff if ((res = deflateInit2(&priv->cx, 26281ccbd95SGleb Smirnoff Z_DEFAULT_COMPRESSION, Z_DEFLATED, 26381ccbd95SGleb Smirnoff -cfg->windowBits, 8, 26481ccbd95SGleb Smirnoff Z_DEFAULT_STRATEGY)) != Z_OK) { 26581ccbd95SGleb Smirnoff log(LOG_NOTICE, 26681ccbd95SGleb Smirnoff "deflateInit2: error %d, %s\n", 26781ccbd95SGleb Smirnoff res, priv->cx.msg); 26881ccbd95SGleb Smirnoff priv->cfg.enable = 0; 26981ccbd95SGleb Smirnoff ERROUT(ENOMEM); 27081ccbd95SGleb Smirnoff } 27181ccbd95SGleb Smirnoff } else { 27281ccbd95SGleb Smirnoff if ((res = inflateInit2(&priv->cx, 27381ccbd95SGleb Smirnoff -cfg->windowBits)) != Z_OK) { 27481ccbd95SGleb Smirnoff log(LOG_NOTICE, 27581ccbd95SGleb Smirnoff "inflateInit2: error %d, %s\n", 27681ccbd95SGleb Smirnoff res, priv->cx.msg); 27781ccbd95SGleb Smirnoff priv->cfg.enable = 0; 27881ccbd95SGleb Smirnoff ERROUT(ENOMEM); 27981ccbd95SGleb Smirnoff } 28081ccbd95SGleb Smirnoff } 28181ccbd95SGleb Smirnoff } 28281ccbd95SGleb Smirnoff 28381ccbd95SGleb Smirnoff /* Initialize other state. */ 28481ccbd95SGleb Smirnoff priv->seqnum = 0; 28581ccbd95SGleb Smirnoff 28681ccbd95SGleb Smirnoff /* Save return address so we can send reset-req's */ 28781ccbd95SGleb Smirnoff priv->ctrlnode = NGI_RETADDR(item); 28881ccbd95SGleb Smirnoff break; 28981ccbd95SGleb Smirnoff } 29081ccbd95SGleb Smirnoff 29181ccbd95SGleb Smirnoff case NGM_DEFLATE_RESETREQ: 29281ccbd95SGleb Smirnoff ng_deflate_reset_req(node); 29381ccbd95SGleb Smirnoff break; 29481ccbd95SGleb Smirnoff 29581ccbd95SGleb Smirnoff case NGM_DEFLATE_GET_STATS: 29681ccbd95SGleb Smirnoff case NGM_DEFLATE_CLR_STATS: 29781ccbd95SGleb Smirnoff case NGM_DEFLATE_GETCLR_STATS: 29881ccbd95SGleb Smirnoff /* Create response if requested. */ 29981ccbd95SGleb Smirnoff if (msg->header.cmd != NGM_DEFLATE_CLR_STATS) { 30081ccbd95SGleb Smirnoff NG_MKRESPONSE(resp, msg, 30181ccbd95SGleb Smirnoff sizeof(struct ng_deflate_stats), M_NOWAIT); 30281ccbd95SGleb Smirnoff if (resp == NULL) 30381ccbd95SGleb Smirnoff ERROUT(ENOMEM); 30481ccbd95SGleb Smirnoff bcopy(&priv->stats, resp->data, 30581ccbd95SGleb Smirnoff sizeof(struct ng_deflate_stats)); 30681ccbd95SGleb Smirnoff } 30781ccbd95SGleb Smirnoff 30881ccbd95SGleb Smirnoff /* Clear stats if requested. */ 30981ccbd95SGleb Smirnoff if (msg->header.cmd != NGM_DEFLATE_GET_STATS) 31081ccbd95SGleb Smirnoff bzero(&priv->stats, 31181ccbd95SGleb Smirnoff sizeof(struct ng_deflate_stats)); 31281ccbd95SGleb Smirnoff break; 31381ccbd95SGleb Smirnoff 31481ccbd95SGleb Smirnoff default: 31581ccbd95SGleb Smirnoff error = EINVAL; 31681ccbd95SGleb Smirnoff break; 31781ccbd95SGleb Smirnoff } 31881ccbd95SGleb Smirnoff done: 31981ccbd95SGleb Smirnoff NG_RESPOND_MSG(error, node, item, resp); 32081ccbd95SGleb Smirnoff NG_FREE_MSG(msg); 32181ccbd95SGleb Smirnoff return (error); 32281ccbd95SGleb Smirnoff } 32381ccbd95SGleb Smirnoff 32481ccbd95SGleb Smirnoff /* 32581ccbd95SGleb Smirnoff * Receive incoming data on our hook. 32681ccbd95SGleb Smirnoff */ 32781ccbd95SGleb Smirnoff static int 32881ccbd95SGleb Smirnoff ng_deflate_rcvdata(hook_p hook, item_p item) 32981ccbd95SGleb Smirnoff { 33081ccbd95SGleb Smirnoff const node_p node = NG_HOOK_NODE(hook); 33181ccbd95SGleb Smirnoff const priv_p priv = NG_NODE_PRIVATE(node); 33281ccbd95SGleb Smirnoff struct mbuf *m, *out; 33381ccbd95SGleb Smirnoff int error; 33481ccbd95SGleb Smirnoff 33581ccbd95SGleb Smirnoff if (!priv->cfg.enable) { 33681ccbd95SGleb Smirnoff NG_FREE_ITEM(item); 33781ccbd95SGleb Smirnoff return (ENXIO); 33881ccbd95SGleb Smirnoff } 33981ccbd95SGleb Smirnoff 34081ccbd95SGleb Smirnoff NGI_GET_M(item, m); 34181ccbd95SGleb Smirnoff /* Compress */ 34281ccbd95SGleb Smirnoff if (priv->compress) { 34381ccbd95SGleb Smirnoff if ((error = ng_deflate_compress(node, m, &out)) != 0) { 34481ccbd95SGleb Smirnoff NG_FREE_ITEM(item); 34581ccbd95SGleb Smirnoff log(LOG_NOTICE, "%s: error: %d\n", __func__, error); 34681ccbd95SGleb Smirnoff return (error); 34781ccbd95SGleb Smirnoff } 34881ccbd95SGleb Smirnoff 34981ccbd95SGleb Smirnoff } else { /* Decompress */ 35081ccbd95SGleb Smirnoff if ((error = ng_deflate_decompress(node, m, &out)) != 0) { 35181ccbd95SGleb Smirnoff NG_FREE_ITEM(item); 35281ccbd95SGleb Smirnoff log(LOG_NOTICE, "%s: error: %d\n", __func__, error); 35381ccbd95SGleb Smirnoff if (priv->ctrlnode != 0) { 35481ccbd95SGleb Smirnoff struct ng_mesg *msg; 35581ccbd95SGleb Smirnoff 35681ccbd95SGleb Smirnoff /* Need to send a reset-request. */ 35781ccbd95SGleb Smirnoff NG_MKMESSAGE(msg, NGM_DEFLATE_COOKIE, 35881ccbd95SGleb Smirnoff NGM_DEFLATE_RESETREQ, 0, M_NOWAIT); 35981ccbd95SGleb Smirnoff if (msg == NULL) 36081ccbd95SGleb Smirnoff return (error); 36181ccbd95SGleb Smirnoff NG_SEND_MSG_ID(error, node, msg, 36281ccbd95SGleb Smirnoff priv->ctrlnode, 0); 36381ccbd95SGleb Smirnoff } 36481ccbd95SGleb Smirnoff return (error); 36581ccbd95SGleb Smirnoff } 36681ccbd95SGleb Smirnoff } 36781ccbd95SGleb Smirnoff 36881ccbd95SGleb Smirnoff NG_FWD_NEW_DATA(error, item, hook, out); 36981ccbd95SGleb Smirnoff return (error); 37081ccbd95SGleb Smirnoff } 37181ccbd95SGleb Smirnoff 37281ccbd95SGleb Smirnoff /* 37381ccbd95SGleb Smirnoff * Destroy node. 37481ccbd95SGleb Smirnoff */ 37581ccbd95SGleb Smirnoff static int 37681ccbd95SGleb Smirnoff ng_deflate_shutdown(node_p node) 37781ccbd95SGleb Smirnoff { 37881ccbd95SGleb Smirnoff const priv_p priv = NG_NODE_PRIVATE(node); 37981ccbd95SGleb Smirnoff 38081ccbd95SGleb Smirnoff /* Take down netgraph node. */ 38181ccbd95SGleb Smirnoff if (priv->cfg.enable) { 38281ccbd95SGleb Smirnoff if (priv->compress) 38381ccbd95SGleb Smirnoff deflateEnd(&priv->cx); 38481ccbd95SGleb Smirnoff else 38581ccbd95SGleb Smirnoff inflateEnd(&priv->cx); 38681ccbd95SGleb Smirnoff } 38781ccbd95SGleb Smirnoff 38881ccbd95SGleb Smirnoff free(priv, M_NETGRAPH_DEFLATE); 38981ccbd95SGleb Smirnoff NG_NODE_SET_PRIVATE(node, NULL); 39081ccbd95SGleb Smirnoff NG_NODE_UNREF(node); /* let the node escape */ 39181ccbd95SGleb Smirnoff return (0); 39281ccbd95SGleb Smirnoff } 39381ccbd95SGleb Smirnoff 39481ccbd95SGleb Smirnoff /* 39581ccbd95SGleb Smirnoff * Hook disconnection 39681ccbd95SGleb Smirnoff */ 39781ccbd95SGleb Smirnoff static int 39881ccbd95SGleb Smirnoff ng_deflate_disconnect(hook_p hook) 39981ccbd95SGleb Smirnoff { 40081ccbd95SGleb Smirnoff const node_p node = NG_HOOK_NODE(hook); 40181ccbd95SGleb Smirnoff const priv_p priv = NG_NODE_PRIVATE(node); 40281ccbd95SGleb Smirnoff 40381ccbd95SGleb Smirnoff if (priv->cfg.enable) { 40481ccbd95SGleb Smirnoff if (priv->compress) 40581ccbd95SGleb Smirnoff deflateEnd(&priv->cx); 40681ccbd95SGleb Smirnoff else 40781ccbd95SGleb Smirnoff inflateEnd(&priv->cx); 40881ccbd95SGleb Smirnoff priv->cfg.enable = 0; 40981ccbd95SGleb Smirnoff } 41081ccbd95SGleb Smirnoff 41181ccbd95SGleb Smirnoff /* Go away if no longer connected. */ 41281ccbd95SGleb Smirnoff if ((NG_NODE_NUMHOOKS(node) == 0) && NG_NODE_IS_VALID(node)) 41381ccbd95SGleb Smirnoff ng_rmnode_self(node); 41481ccbd95SGleb Smirnoff return (0); 41581ccbd95SGleb Smirnoff } 41681ccbd95SGleb Smirnoff 41781ccbd95SGleb Smirnoff /************************************************************************ 41881ccbd95SGleb Smirnoff HELPER STUFF 41981ccbd95SGleb Smirnoff ************************************************************************/ 42081ccbd95SGleb Smirnoff 42181ccbd95SGleb Smirnoff /* 42281ccbd95SGleb Smirnoff * Space allocation and freeing routines for use by zlib routines. 42381ccbd95SGleb Smirnoff */ 42481ccbd95SGleb Smirnoff 42581ccbd95SGleb Smirnoff static void * 42681ccbd95SGleb Smirnoff z_alloc(void *notused, u_int items, u_int size) 42781ccbd95SGleb Smirnoff { 42881ccbd95SGleb Smirnoff 42981ccbd95SGleb Smirnoff return (malloc(items * size, M_NETGRAPH_DEFLATE, M_NOWAIT)); 43081ccbd95SGleb Smirnoff } 43181ccbd95SGleb Smirnoff 43281ccbd95SGleb Smirnoff static void 43381ccbd95SGleb Smirnoff z_free(void *notused, void *ptr) 43481ccbd95SGleb Smirnoff { 43581ccbd95SGleb Smirnoff 43681ccbd95SGleb Smirnoff free(ptr, M_NETGRAPH_DEFLATE); 43781ccbd95SGleb Smirnoff } 43881ccbd95SGleb Smirnoff 43981ccbd95SGleb Smirnoff /* 44081ccbd95SGleb Smirnoff * Compress/encrypt a packet and put the result in a new mbuf at *resultp. 44181ccbd95SGleb Smirnoff * The original mbuf is not free'd. 44281ccbd95SGleb Smirnoff */ 44381ccbd95SGleb Smirnoff static int 44481ccbd95SGleb Smirnoff ng_deflate_compress(node_p node, struct mbuf *m, struct mbuf **resultp) 44581ccbd95SGleb Smirnoff { 44681ccbd95SGleb Smirnoff const priv_p priv = NG_NODE_PRIVATE(node); 44781ccbd95SGleb Smirnoff int outlen, inlen; 44881ccbd95SGleb Smirnoff int rtn; 44981ccbd95SGleb Smirnoff 45081ccbd95SGleb Smirnoff /* Initialize. */ 45181ccbd95SGleb Smirnoff *resultp = NULL; 45281ccbd95SGleb Smirnoff 45381ccbd95SGleb Smirnoff inlen = m->m_pkthdr.len; 45481ccbd95SGleb Smirnoff 45581ccbd95SGleb Smirnoff priv->stats.FramesPlain++; 45681ccbd95SGleb Smirnoff priv->stats.InOctets+=inlen; 45781ccbd95SGleb Smirnoff 45881ccbd95SGleb Smirnoff if (inlen > DEFLATE_BUF_SIZE) { 45981ccbd95SGleb Smirnoff priv->stats.Errors++; 46081ccbd95SGleb Smirnoff NG_FREE_M(m); 46181ccbd95SGleb Smirnoff return (ENOMEM); 46281ccbd95SGleb Smirnoff } 46381ccbd95SGleb Smirnoff 464e4651e05SAlexander Motin /* We must own the mbuf chain exclusively to modify it. */ 465*eb1b1807SGleb Smirnoff m = m_unshare(m, M_NOWAIT); 466e4651e05SAlexander Motin if (m == NULL) { 467e4651e05SAlexander Motin priv->stats.Errors++; 468e4651e05SAlexander Motin return (ENOMEM); 469e4651e05SAlexander Motin } 470e4651e05SAlexander Motin 47181ccbd95SGleb Smirnoff /* Work with contiguous regions of memory. */ 47281ccbd95SGleb Smirnoff m_copydata(m, 0, inlen, (caddr_t)priv->inbuf); 47381ccbd95SGleb Smirnoff outlen = DEFLATE_BUF_SIZE; 47481ccbd95SGleb Smirnoff 47581ccbd95SGleb Smirnoff /* Compress "inbuf" into "outbuf". */ 47681ccbd95SGleb Smirnoff /* Prepare to compress. */ 47781ccbd95SGleb Smirnoff if (priv->inbuf[0] != 0) { 47881ccbd95SGleb Smirnoff priv->cx.next_in = priv->inbuf; 47981ccbd95SGleb Smirnoff priv->cx.avail_in = inlen; 48081ccbd95SGleb Smirnoff } else { 48181ccbd95SGleb Smirnoff priv->cx.next_in = priv->inbuf + 1; /* compress protocol */ 48281ccbd95SGleb Smirnoff priv->cx.avail_in = inlen - 1; 48381ccbd95SGleb Smirnoff } 48481ccbd95SGleb Smirnoff priv->cx.next_out = priv->outbuf + 2 + DEFLATE_HDRLEN; 48581ccbd95SGleb Smirnoff priv->cx.avail_out = outlen - 2 - DEFLATE_HDRLEN; 48681ccbd95SGleb Smirnoff 48781ccbd95SGleb Smirnoff /* Compress. */ 48881ccbd95SGleb Smirnoff rtn = deflate(&priv->cx, Z_PACKET_FLUSH); 48981ccbd95SGleb Smirnoff 49081ccbd95SGleb Smirnoff /* Check return value. */ 49181ccbd95SGleb Smirnoff if (rtn != Z_OK) { 49281ccbd95SGleb Smirnoff priv->stats.Errors++; 49381ccbd95SGleb Smirnoff log(LOG_NOTICE, "ng_deflate: compression error: %d (%s)\n", 49481ccbd95SGleb Smirnoff rtn, priv->cx.msg); 49581ccbd95SGleb Smirnoff NG_FREE_M(m); 49681ccbd95SGleb Smirnoff return (EINVAL); 49781ccbd95SGleb Smirnoff } 49881ccbd95SGleb Smirnoff 49981ccbd95SGleb Smirnoff /* Calculate resulting size. */ 50081ccbd95SGleb Smirnoff outlen -= priv->cx.avail_out; 50181ccbd95SGleb Smirnoff 50281ccbd95SGleb Smirnoff /* If we can't compress this packet, send it as-is. */ 50381ccbd95SGleb Smirnoff if (outlen > inlen) { 50481ccbd95SGleb Smirnoff /* Return original packet uncompressed. */ 50581ccbd95SGleb Smirnoff *resultp = m; 50681ccbd95SGleb Smirnoff priv->stats.FramesUncomp++; 50781ccbd95SGleb Smirnoff priv->stats.OutOctets+=inlen; 50881ccbd95SGleb Smirnoff } else { 50981ccbd95SGleb Smirnoff /* Install header. */ 510b652a5faSAlexander Motin be16enc(priv->outbuf, PROT_COMPD); 511b652a5faSAlexander Motin be16enc(priv->outbuf + 2, priv->seqnum); 51281ccbd95SGleb Smirnoff 51381ccbd95SGleb Smirnoff /* Return packet in an mbuf. */ 514e4651e05SAlexander Motin m_copyback(m, 0, outlen, (caddr_t)priv->outbuf); 515e4651e05SAlexander Motin if (m->m_pkthdr.len < outlen) { 516e4651e05SAlexander Motin m_freem(m); 51781ccbd95SGleb Smirnoff priv->stats.Errors++; 51881ccbd95SGleb Smirnoff return (ENOMEM); 519e4651e05SAlexander Motin } else if (outlen < m->m_pkthdr.len) 520e4651e05SAlexander Motin m_adj(m, outlen - m->m_pkthdr.len); 521e4651e05SAlexander Motin *resultp = m; 52281ccbd95SGleb Smirnoff priv->stats.FramesComp++; 52381ccbd95SGleb Smirnoff priv->stats.OutOctets+=outlen; 52481ccbd95SGleb Smirnoff } 52581ccbd95SGleb Smirnoff 52681ccbd95SGleb Smirnoff /* Update sequence number. */ 52781ccbd95SGleb Smirnoff priv->seqnum++; 52881ccbd95SGleb Smirnoff 52981ccbd95SGleb Smirnoff return (0); 53081ccbd95SGleb Smirnoff } 53181ccbd95SGleb Smirnoff 53281ccbd95SGleb Smirnoff /* 53381ccbd95SGleb Smirnoff * Decompress/decrypt packet and put the result in a new mbuf at *resultp. 53481ccbd95SGleb Smirnoff * The original mbuf is not free'd. 53581ccbd95SGleb Smirnoff */ 53681ccbd95SGleb Smirnoff static int 53781ccbd95SGleb Smirnoff ng_deflate_decompress(node_p node, struct mbuf *m, struct mbuf **resultp) 53881ccbd95SGleb Smirnoff { 53981ccbd95SGleb Smirnoff const priv_p priv = NG_NODE_PRIVATE(node); 54081ccbd95SGleb Smirnoff int outlen, inlen; 54181ccbd95SGleb Smirnoff int rtn; 54281ccbd95SGleb Smirnoff uint16_t proto; 54381ccbd95SGleb Smirnoff int offset; 54481ccbd95SGleb Smirnoff uint16_t rseqnum; 54581ccbd95SGleb Smirnoff 54681ccbd95SGleb Smirnoff /* Initialize. */ 54781ccbd95SGleb Smirnoff *resultp = NULL; 54881ccbd95SGleb Smirnoff 54981ccbd95SGleb Smirnoff inlen = m->m_pkthdr.len; 55081ccbd95SGleb Smirnoff 55181ccbd95SGleb Smirnoff if (inlen > DEFLATE_BUF_SIZE) { 55281ccbd95SGleb Smirnoff priv->stats.Errors++; 55381ccbd95SGleb Smirnoff NG_FREE_M(m); 55481ccbd95SGleb Smirnoff priv->seqnum = 0; 55581ccbd95SGleb Smirnoff return (ENOMEM); 55681ccbd95SGleb Smirnoff } 55781ccbd95SGleb Smirnoff 558e4651e05SAlexander Motin /* We must own the mbuf chain exclusively to modify it. */ 559*eb1b1807SGleb Smirnoff m = m_unshare(m, M_NOWAIT); 560e4651e05SAlexander Motin if (m == NULL) { 561e4651e05SAlexander Motin priv->stats.Errors++; 562e4651e05SAlexander Motin return (ENOMEM); 563e4651e05SAlexander Motin } 564e4651e05SAlexander Motin 56581ccbd95SGleb Smirnoff /* Work with contiguous regions of memory. */ 56681ccbd95SGleb Smirnoff m_copydata(m, 0, inlen, (caddr_t)priv->inbuf); 56781ccbd95SGleb Smirnoff 56881ccbd95SGleb Smirnoff /* Separate proto. */ 56981ccbd95SGleb Smirnoff if ((priv->inbuf[0] & 0x01) != 0) { 57081ccbd95SGleb Smirnoff proto = priv->inbuf[0]; 57181ccbd95SGleb Smirnoff offset = 1; 57281ccbd95SGleb Smirnoff } else { 573b652a5faSAlexander Motin proto = be16dec(priv->inbuf); 57481ccbd95SGleb Smirnoff offset = 2; 57581ccbd95SGleb Smirnoff } 57681ccbd95SGleb Smirnoff 577833c4a01SGleb Smirnoff priv->stats.InOctets += inlen; 578833c4a01SGleb Smirnoff 57981ccbd95SGleb Smirnoff /* Packet is compressed, so decompress. */ 58081ccbd95SGleb Smirnoff if (proto == PROT_COMPD) { 58181ccbd95SGleb Smirnoff priv->stats.FramesComp++; 58281ccbd95SGleb Smirnoff 58381ccbd95SGleb Smirnoff /* Check sequence number. */ 584b652a5faSAlexander Motin rseqnum = be16dec(priv->inbuf + offset); 58581ccbd95SGleb Smirnoff offset += 2; 58681ccbd95SGleb Smirnoff if (rseqnum != priv->seqnum) { 58781ccbd95SGleb Smirnoff priv->stats.Errors++; 58881ccbd95SGleb Smirnoff log(LOG_NOTICE, "ng_deflate: wrong sequence: %u " 58981ccbd95SGleb Smirnoff "instead of %u\n", rseqnum, priv->seqnum); 59081ccbd95SGleb Smirnoff NG_FREE_M(m); 59181ccbd95SGleb Smirnoff priv->seqnum = 0; 59281ccbd95SGleb Smirnoff return (EPIPE); 59381ccbd95SGleb Smirnoff } 59481ccbd95SGleb Smirnoff 59581ccbd95SGleb Smirnoff outlen = DEFLATE_BUF_SIZE; 59681ccbd95SGleb Smirnoff 59781ccbd95SGleb Smirnoff /* Decompress "inbuf" into "outbuf". */ 59881ccbd95SGleb Smirnoff /* Prepare to decompress. */ 59981ccbd95SGleb Smirnoff priv->cx.next_in = priv->inbuf + offset; 60081ccbd95SGleb Smirnoff priv->cx.avail_in = inlen - offset; 60181ccbd95SGleb Smirnoff /* Reserve space for protocol decompression. */ 60281ccbd95SGleb Smirnoff priv->cx.next_out = priv->outbuf + 1; 60381ccbd95SGleb Smirnoff priv->cx.avail_out = outlen - 1; 60481ccbd95SGleb Smirnoff 60581ccbd95SGleb Smirnoff /* Decompress. */ 60681ccbd95SGleb Smirnoff rtn = inflate(&priv->cx, Z_PACKET_FLUSH); 60781ccbd95SGleb Smirnoff 60881ccbd95SGleb Smirnoff /* Check return value. */ 60981ccbd95SGleb Smirnoff if (rtn != Z_OK && rtn != Z_STREAM_END) { 61081ccbd95SGleb Smirnoff priv->stats.Errors++; 61181ccbd95SGleb Smirnoff NG_FREE_M(m); 61281ccbd95SGleb Smirnoff priv->seqnum = 0; 61381ccbd95SGleb Smirnoff log(LOG_NOTICE, "%s: decompression error: %d (%s)\n", 61481ccbd95SGleb Smirnoff __func__, rtn, priv->cx.msg); 61581ccbd95SGleb Smirnoff 61681ccbd95SGleb Smirnoff switch (rtn) { 61781ccbd95SGleb Smirnoff case Z_MEM_ERROR: 61881ccbd95SGleb Smirnoff return (ENOMEM); 61981ccbd95SGleb Smirnoff case Z_DATA_ERROR: 62081ccbd95SGleb Smirnoff return (EIO); 62181ccbd95SGleb Smirnoff default: 62281ccbd95SGleb Smirnoff return (EINVAL); 62381ccbd95SGleb Smirnoff } 62481ccbd95SGleb Smirnoff } 62581ccbd95SGleb Smirnoff 62681ccbd95SGleb Smirnoff /* Calculate resulting size. */ 62781ccbd95SGleb Smirnoff outlen -= priv->cx.avail_out; 62881ccbd95SGleb Smirnoff 62981ccbd95SGleb Smirnoff /* Decompress protocol. */ 63081ccbd95SGleb Smirnoff if ((priv->outbuf[1] & 0x01) != 0) { 63181ccbd95SGleb Smirnoff priv->outbuf[0] = 0; 63281ccbd95SGleb Smirnoff /* Return packet in an mbuf. */ 633e4651e05SAlexander Motin m_copyback(m, 0, outlen, (caddr_t)priv->outbuf); 63481ccbd95SGleb Smirnoff } else { 63581ccbd95SGleb Smirnoff outlen--; 63681ccbd95SGleb Smirnoff /* Return packet in an mbuf. */ 637e4651e05SAlexander Motin m_copyback(m, 0, outlen, (caddr_t)(priv->outbuf + 1)); 63881ccbd95SGleb Smirnoff } 639e4651e05SAlexander Motin if (m->m_pkthdr.len < outlen) { 640e4651e05SAlexander Motin m_freem(m); 64181ccbd95SGleb Smirnoff priv->stats.Errors++; 64281ccbd95SGleb Smirnoff priv->seqnum = 0; 64381ccbd95SGleb Smirnoff return (ENOMEM); 644e4651e05SAlexander Motin } else if (outlen < m->m_pkthdr.len) 645e4651e05SAlexander Motin m_adj(m, outlen - m->m_pkthdr.len); 646e4651e05SAlexander Motin *resultp = m; 64781ccbd95SGleb Smirnoff priv->stats.FramesPlain++; 64881ccbd95SGleb Smirnoff priv->stats.OutOctets+=outlen; 64981ccbd95SGleb Smirnoff 65081ccbd95SGleb Smirnoff } else { /* Packet is not compressed, just update dictionary. */ 65181ccbd95SGleb Smirnoff priv->stats.FramesUncomp++; 65281ccbd95SGleb Smirnoff if (priv->inbuf[0] == 0) { 65381ccbd95SGleb Smirnoff priv->cx.next_in = priv->inbuf + 1; /* compress protocol */ 65481ccbd95SGleb Smirnoff priv->cx.avail_in = inlen - 1; 65581ccbd95SGleb Smirnoff } else { 65681ccbd95SGleb Smirnoff priv->cx.next_in = priv->inbuf; 65781ccbd95SGleb Smirnoff priv->cx.avail_in = inlen; 65881ccbd95SGleb Smirnoff } 65981ccbd95SGleb Smirnoff 66081ccbd95SGleb Smirnoff rtn = inflateIncomp(&priv->cx); 66181ccbd95SGleb Smirnoff 66281ccbd95SGleb Smirnoff /* Check return value */ 66381ccbd95SGleb Smirnoff if (rtn != Z_OK) { 66481ccbd95SGleb Smirnoff priv->stats.Errors++; 66581ccbd95SGleb Smirnoff log(LOG_NOTICE, "%s: inflateIncomp error: %d (%s)\n", 66681ccbd95SGleb Smirnoff __func__, rtn, priv->cx.msg); 66781ccbd95SGleb Smirnoff NG_FREE_M(m); 66881ccbd95SGleb Smirnoff priv->seqnum = 0; 66981ccbd95SGleb Smirnoff return (EINVAL); 67081ccbd95SGleb Smirnoff } 67181ccbd95SGleb Smirnoff 67281ccbd95SGleb Smirnoff *resultp = m; 67381ccbd95SGleb Smirnoff priv->stats.FramesPlain++; 67481ccbd95SGleb Smirnoff priv->stats.OutOctets += inlen; 67581ccbd95SGleb Smirnoff } 67681ccbd95SGleb Smirnoff 67781ccbd95SGleb Smirnoff /* Update sequence number. */ 67881ccbd95SGleb Smirnoff priv->seqnum++; 67981ccbd95SGleb Smirnoff 68081ccbd95SGleb Smirnoff return (0); 68181ccbd95SGleb Smirnoff } 68281ccbd95SGleb Smirnoff 68381ccbd95SGleb Smirnoff /* 68481ccbd95SGleb Smirnoff * The peer has sent us a CCP ResetRequest, so reset our transmit state. 68581ccbd95SGleb Smirnoff */ 68681ccbd95SGleb Smirnoff static void 68781ccbd95SGleb Smirnoff ng_deflate_reset_req(node_p node) 68881ccbd95SGleb Smirnoff { 68981ccbd95SGleb Smirnoff const priv_p priv = NG_NODE_PRIVATE(node); 69081ccbd95SGleb Smirnoff 69181ccbd95SGleb Smirnoff priv->seqnum = 0; 69281ccbd95SGleb Smirnoff if (priv->cfg.enable) { 69381ccbd95SGleb Smirnoff if (priv->compress) 69481ccbd95SGleb Smirnoff deflateReset(&priv->cx); 69581ccbd95SGleb Smirnoff else 69681ccbd95SGleb Smirnoff inflateReset(&priv->cx); 69781ccbd95SGleb Smirnoff } 69881ccbd95SGleb Smirnoff } 69981ccbd95SGleb Smirnoff 700