1/* --------------------------------------------------------------------------------------------------------------
2 * FILE NAME       l7_filter_main.c (for Linux Platform)
3 * DATE            10/25/2007
4 * AUTHOR/S        Max Ding
5 * Description     Layer 7 filter
6 * --------------------------------------------------------------------------------------------------------------
7 */
8
9//MODULE_AUTHOR("Matthew Strait <quadong@users.sf.net>, Ethan Sommer <sommere@users.sf.net>");
10//MODULE_LICENSE("GPL");
11//MODULE_DESCRIPTION("application layer match module");
12
13/*******include start*******/
14#ifdef __LINUX__
15#include <linux/module.h>
16#include <linux/if.h>
17#include <linux/types.h>
18#include <linux/in.h>
19#include <linux/ip.h>
20#include <linux/skbuff.h>
21#include <linux/acosnatfw.h>
22#include <linux/netdevice.h>
23#include <linux/inetdevice.h>
24#include <linux/mm.h>
25
26#ifdef CONFIG_NETFILTER
27#include <linux/netfilter.h>
28#include <linux/netfilter_ipv4.h>
29#endif /* CONFIG_NETFILTER */
30
31#include "bcmnvram.h"
32#endif /* __LINUX__ */
33
34/* #define L7_DEBUG_ON */
35
36#include "regexp/regexp.c"
37#include "l7_filter_main.h"
38
39/*******define start*******/
40#ifdef L7_DEBUG_ON
41#define L7_DEBUG_PRI_0 0x00000000
42#define L7_DEBUG_PRI_1 0x00000001
43#define L7_DEBUG_PRI_2 0x00000002
44#define L7_DEBUG_PRI_3 0x00000004
45#define L7_DEBUG_PRI_4 0x00000008
46#define L7_DEBUG_PRI_5 0x00000010
47#define L7_DEBUG_PRI_6 0x00000020
48#define L7_DEBUG_PRI_7 0x00000040
49#define L7_DEBUG_PRI_a 0xffffffff
50#define L7_PRINTK_DEBUG printk
51#define L7_PRINTK_DEBUG_1 if (debug_pri & L7_DEBUG_PRI_1) printk
52#define L7_PRINTK_DEBUG_2 if (debug_pri & L7_DEBUG_PRI_2) printk
53#define L7_PRINTK_DEBUG_3 if (debug_pri & L7_DEBUG_PRI_3) printk
54#define L7_PRINTK_DEBUG_4 if (debug_pri & L7_DEBUG_PRI_4) printk
55#define L7_PRINTK_DEBUG_5 if (debug_pri & L7_DEBUG_PRI_5) printk
56#define L7_PRINTK_DEBUG_6 if (debug_pri & L7_DEBUG_PRI_6) printk
57#define L7_PRINTK_DEBUG_7 if (debug_pri & L7_DEBUG_PRI_7) printk
58
59#else/* else for #ifdef L7_DEBUG_ON */
60
61#define L7_PRINTK_DEBUG printk
62#define L7_PRINTK_DEBUG_1 printk
63#define L7_PRINTK_DEBUG_2 printk
64#define L7_PRINTK_DEBUG_3 printk
65#define L7_PRINTK_DEBUG_4 printk
66#define L7_PRINTK_DEBUG_5 printk
67#define L7_PRINTK_DEBUG_6 printk
68#define L7_PRINTK_DEBUG_7 printk
69#endif/* end for #ifdef L7_DEBUG_ON */
70
71/*******global and static variables start*******/
72#ifdef L7_DEBUG_ON
73static int debug_pri = 0x00000001;
74MODULE_PARM(debug_pri, "i");
75#endif
76
77/* Number of packets whose data we look at. */
78static int num_packets = 20;
79
80/* Fxcn port-S Wins, 0717-09 */
81static char tolower(char c);
82/* Fxcn port-E Wins, 0717-09 */
83
84/* head of the link list of patterns */
85static s_pattern_cache * first_pattern_cache = NULL;
86
87#ifdef CONFIG_NETFILTER
88int l7_filter_main(struct sk_buff *skb, int iDirection);
89static unsigned int l7_filter_hook(unsigned int hook,
90                                    struct sk_buff **skb,
91                                    const struct net_device *in,
92                                    const struct net_device *out,
93                                    int (*okfn)(struct sk_buff *))
94{
95    int dir;
96
97    if (hook == NF_IP_PRE_ROUTING)
98        dir = 0;
99    else if (hook == NF_IP_POST_ROUTING)
100        dir = 1;
101    else
102        return NF_ACCEPT;
103
104    local_bh_disable();
105    l7_filter_main(*skb, dir);
106    local_bh_enable();
107
108    return NF_ACCEPT;
109}
110
111static struct nf_hook_ops l7_filter_prerouting_ops = {
112#ifdef AG_LINUX_26
113    .owner          = THIS_MODULE,
114#else
115    .list           = {NULL, NULL},
116#endif /* AG_LINUX_26 */
117    .hook           = l7_filter_hook,
118    .pf             = PF_INET,
119    .hooknum        = NF_IP_PRE_ROUTING,
120    .priority       = NF_IP_PRI_LAST,
121};
122static struct nf_hook_ops l7_filter_postrouting_ops = {
123#ifdef AG_LINUX_26
124    .owner          = THIS_MODULE,
125#else
126    .list           = {NULL, NULL},
127#endif /* AG_LINUX_26 */
128    .hook           = l7_filter_hook,
129    .pf             = PF_INET,
130    .hooknum        = NF_IP_POST_ROUTING,
131    .priority       = NF_IP_PRI_LAST,
132};
133#endif /* CONFIG_NETFILTER */
134
135/* Hard code for all expression of protocol. It's better to read them from *.pat files. */
136static s_proto_regexp g_all_proto[] =
137{
138    {"bittorrent", L7_ENUM_BITTORRENT, 0, "^(\\x13bittorrent protocol|azver\\x01$|get /scrape\\?info_hash=)|d1:ad2:id20:|\\x08'7P\\)[RP]"},
139    {"fasttrack", L7_ENUM_FASTTRACK, 0, "^get (/.download/[ -~]*|/.supernode[ -~]|/.status[ -~]|/.network[ -~]*|/.files|/.hash=[0-9a-f]*/[ -~]*) http/1.1|user-agent: kazaa|x-kazaa(-username|-network|-ip|-supernodeip|-xferid|-xferuid|tag)|^give [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]?[0-9]?[0-9]?"},
140    {"edonkey", L7_ENUM_EDONKEY, 0, "^[\\xc5\\xd4\\xe3-\\xe5].?.?.?.?([\\x01\\x02\\x05\\x14\\x15\\x16\\x18\\x19\\x1a\\x1b\\x1c\\x20\\x21\\x32\\x33\\x34\\x35\\x36\\x38\\x40\\x41\\x42\\x43\\x46\\x47\\x48\\x49\\x4a\\x4b\\x4c\\x4d\\x4e\\x4f\\x50\\x51\\x52\\x53\\x54\\x55\\x56\\x57\\x58[\\x60\\x81\\x82\\x90\\x91\\x93\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9e\\xa0\\xa1\\xa2\\xa3\\xa4]|\\x59................?[ -~]|\\x96....$)"},
141    {"gnutella", L7_ENUM_GNUTELLA, 0, "^(gnd[\\x01\\x02]?.?.?\\x01|gnutella connect/[012]\\.[0-9]\\x0d\\x0a|get /uri-res/n2r\\?urn:sha1:|get /.*user-agent: (gtk-gnutella|bearshare|mactella|gnucleus|gnotella|limewire|imesh)|get /.*content-type: application/x-gnutella-packets|giv [0-9]*:[0-9a-f]*/|queue [0-9a-f]* [1-9][0-9]?[0-9]?\\.[1-9][0-9]?[0-9]?\\.[1-9][0-9]?[0-9]?\\.[1-9][0-9]?[0-9]?:[1-9][0-9]?[0-9]?[0-9]?|gnutella.*content-type: application/x-gnutella|...................?lime)"},
142    {"skypetoskype", L7_ENUM_SKYPETOSKYPE, 0, "^..\\x02............."},
143    {"skypeout", L7_ENUM_SKYPEOUT, 0, "^(\\x01.?.?.?.?.?.?.?.?\\x01|\\x02.?.?.?.?.?.?.?.?\\x02|\\x03.?.?.?.?.?.?.?.?\\x03|\\x04.?.?.?.?.?.?.?.?\\x04|\\x05.?.?.?.?.?.?.?.?\\x05|\\x06.?.?.?.?.?.?.?.?\\x06|\\x07.?.?.?.?.?.?.?.?\\x07|\\x08.?.?.?.?.?.?.?.?\\x08|\\x09.?.?.?.?.?.?.?.?\\x09|\\x0a.?.?.?.?.?.?.?.?\\x0a|\\x0b.?.?.?.?.?.?.?.?\\x0b|\\x0c.?.?.?.?.?.?.?.?\\x0c|\\x0d.?.?.?.?.?.?.?.?\\x0d|\\x0e.?.?.?.?.?.?.?.?\\x0e|\\x0f.?.?.?.?.?.?.?.?\\x0f|\\x10.?.?.?.?.?.?.?.?\\x10|\\x11.?.?.?.?.?.?.?.?\\x11|\\x12.?.?.?.?.?.?.?.?\\x12\
144|\\x13.?.?.?.?.?.?.?.?\\x13|\\x14.?.?.?.?.?.?.?.?\\x14|\\x15.?.?.?.?.?.?.?.?\\x15|\\x16.?.?.?.?.?.?.?.?\\x16|\\x17.?.?.?.?.?.?.?.?\\x17|\\x18.?.?.?.?.?.?.?.?\\x18|\\x19.?.?.?.?.?.?.?.?\\x19|\\x1a.?.?.?.?.?.?.?.?\\x1a|\\x1b.?.?.?.?.?.?.?.?\\x1b|\\x1c.?.?.?.?.?.?.?.?\\x1c|\\x1d.?.?.?.?.?.?.?.?\\x1d|\\x1e.?.?.?.?.?.?.?.?\\x1e|\\x1f.?.?.?.?.?.?.?.?\\x1f|\\x20.?.?.?.?.?.?.?.?\\x20|\\x21.?.?.?.?.?.?.?.?\\x21|\\x22.?.?.?.?.?.?.?.?\\x22|\\x23.?.?.?.?.?.?.?.?\\x23|\\$.?.?.?.?.?.?.?.?\\$|\\x25.?.?.?.?.?.?.?.?\\x25\
145|\\x26.?.?.?.?.?.?.?.?\\x26|\\x27.?.?.?.?.?.?.?.?\\x27|\\(.?.?.?.?.?.?.?.?\\(|\\).?.?.?.?.?.?.?.?\\)|\\*.?.?.?.?.?.?.?.?\\*|\\+.?.?.?.?.?.?.?.?\\+|\\x2c.?.?.?.?.?.?.?.?\\x2c|\\x2d.?.?.?.?.?.?.?.?\\x2d|\\..?.?.?.?.?.?.?.?\\.|\\x2f.?.?.?.?.?.?.?.?\\x2f|\\x30.?.?.?.?.?.?.?.?\\x30|\\x31.?.?.?.?.?.?.?.?\\x31|\\x32.?.?.?.?.?.?.?.?\\x32|\\x33.?.?.?.?.?.?.?.?\\x33|\\x34.?.?.?.?.?.?.?.?\\x34|\\x35.?.?.?.?.?.?.?.?\\x35|\\x36.?.?.?.?.?.?.?.?\\x36|\\x37.?.?.?.?.?.?.?.?\\x37|\\x38.?.?.?.?.?.?.?.?\\x38\
146|\\x39.?.?.?.?.?.?.?.?\\x39|\\x3a.?.?.?.?.?.?.?.?\\x3a|\\x3b.?.?.?.?.?.?.?.?\\x3b|\\x3c.?.?.?.?.?.?.?.?\\x3c|\\x3d.?.?.?.?.?.?.?.?\\x3d|\\x3e.?.?.?.?.?.?.?.?\\x3e|\\?.?.?.?.?.?.?.?.?\\?|\\x40.?.?.?.?.?.?.?.?\\x40|\\x41.?.?.?.?.?.?.?.?\\x41|\\x42.?.?.?.?.?.?.?.?\\x42|\\x43.?.?.?.?.?.?.?.?\\x43|\\x44.?.?.?.?.?.?.?.?\\x44|\\x45.?.?.?.?.?.?.?.?\\x45|\\x46.?.?.?.?.?.?.?.?\\x46|\\x47.?.?.?.?.?.?.?.?\\x47|\\x48.?.?.?.?.?.?.?.?\\x48|\\x49.?.?.?.?.?.?.?.?\\x49|\\x4a.?.?.?.?.?.?.?.?\\x4a|\\x4b.?.?.?.?.?.?.?.?\\x4b\
147|\\x4c.?.?.?.?.?.?.?.?\\x4c|\\x4d.?.?.?.?.?.?.?.?\\x4d|\\x4e.?.?.?.?.?.?.?.?\\x4e|\\x4f.?.?.?.?.?.?.?.?\\x4f|\\x50.?.?.?.?.?.?.?.?\\x50|\\x51.?.?.?.?.?.?.?.?\\x51|\\x52.?.?.?.?.?.?.?.?\\x52|\\x53.?.?.?.?.?.?.?.?\\x53|\\x54.?.?.?.?.?.?.?.?\\x54|\\x55.?.?.?.?.?.?.?.?\\x55|\\x56.?.?.?.?.?.?.?.?\\x56|\\x57.?.?.?.?.?.?.?.?\\x57|\\x58.?.?.?.?.?.?.?.?\\x58|\\x59.?.?.?.?.?.?.?.?\\x59|\\x5a.?.?.?.?.?.?.?.?\\x5a|\\[.?.?.?.?.?.?.?.?\\[|\\\\.?.?.?.?.?.?.?.?\\\\|\\].?.?.?.?.?.?.?.?\\]|\\^.?.?.?.?.?.?.?.?\\^\
148|\\x5f.?.?.?.?.?.?.?.?\\x5f|\\x60.?.?.?.?.?.?.?.?\\x60|\\x61.?.?.?.?.?.?.?.?\\x61|\\x62.?.?.?.?.?.?.?.?\\x62|\\x63.?.?.?.?.?.?.?.?\\x63|\\x64.?.?.?.?.?.?.?.?\\x64|\\x65.?.?.?.?.?.?.?.?\\x65|\\x66.?.?.?.?.?.?.?.?\\x66|\\x67.?.?.?.?.?.?.?.?\\x67|\\x68.?.?.?.?.?.?.?.?\\x68|\\x69.?.?.?.?.?.?.?.?\\x69|\\x6a.?.?.?.?.?.?.?.?\\x6a|\\x6b.?.?.?.?.?.?.?.?\\x6b|\\x6c.?.?.?.?.?.?.?.?\\x6c|\\x6d.?.?.?.?.?.?.?.?\\x6d|\\x6e.?.?.?.?.?.?.?.?\\x6e|\\x6f.?.?.?.?.?.?.?.?\\x6f|\\x70.?.?.?.?.?.?.?.?\\x70|\\x71.?.?.?.?.?.?.?.?\\x71\
149|\\x72.?.?.?.?.?.?.?.?\\x72|\\x73.?.?.?.?.?.?.?.?\\x73|\\x74.?.?.?.?.?.?.?.?\\x74|\\x75.?.?.?.?.?.?.?.?\\x75|\\x76.?.?.?.?.?.?.?.?\\x76|\\x77.?.?.?.?.?.?.?.?\\x77|\\x78.?.?.?.?.?.?.?.?\\x78|\\x79.?.?.?.?.?.?.?.?\\x79|\\x7a.?.?.?.?.?.?.?.?\\x7a|\\{.?.?.?.?.?.?.?.?\\{|\\|.?.?.?.?.?.?.?.?\\||\\}.?.?.?.?.?.?.?.?\\}|\\x7e.?.?.?.?.?.?.?.?\\x7e|\\x7f.?.?.?.?.?.?.?.?\\x7f|\\x80.?.?.?.?.?.?.?.?\\x80|\\x81.?.?.?.?.?.?.?.?\\x81|\\x82.?.?.?.?.?.?.?.?\\x82|\\x83.?.?.?.?.?.?.?.?\\x83|\\x84.?.?.?.?.?.?.?.?\\x84|\\x85.?.?.?.?.?.?.?.?\\x85\
150|\\x86.?.?.?.?.?.?.?.?\\x86|\\x87.?.?.?.?.?.?.?.?\\x87|\\x88.?.?.?.?.?.?.?.?\\x88|\\x89.?.?.?.?.?.?.?.?\\x89|\\x8a.?.?.?.?.?.?.?.?\\x8a|\\x8b.?.?.?.?.?.?.?.?\\x8b|\\x8c.?.?.?.?.?.?.?.?\\x8c|\\x8d.?.?.?.?.?.?.?.?\\x8d|\\x8e.?.?.?.?.?.?.?.?\\x8e|\\x8f.?.?.?.?.?.?.?.?\\x8f|\\x90.?.?.?.?.?.?.?.?\\x90|\\x91.?.?.?.?.?.?.?.?\\x91|\\x92.?.?.?.?.?.?.?.?\\x92|\\x93.?.?.?.?.?.?.?.?\\x93|\\x94.?.?.?.?.?.?.?.?\\x94|\\x95.?.?.?.?.?.?.?.?\\x95|\\x96.?.?.?.?.?.?.?.?\\x96|\\x97.?.?.?.?.?.?.?.?\\x97|\\x98.?.?.?.?.?.?.?.?\\x98|\\x99.?.?.?.?.?.?.?.?\\x99\
151|\\x9a.?.?.?.?.?.?.?.?\\x9a|\\x9b.?.?.?.?.?.?.?.?\\x9b|\\x9c.?.?.?.?.?.?.?.?\\x9c|\\x9d.?.?.?.?.?.?.?.?\\x9d|\\x9e.?.?.?.?.?.?.?.?\\x9e|\\x9f.?.?.?.?.?.?.?.?\\x9f|\\xa0.?.?.?.?.?.?.?.?\\xa0|\\xa1.?.?.?.?.?.?.?.?\\xa1|\\xa2.?.?.?.?.?.?.?.?\\xa2|\\xa3.?.?.?.?.?.?.?.?\\xa3|\\xa4.?.?.?.?.?.?.?.?\\xa4|\\xa5.?.?.?.?.?.?.?.?\\xa5|\\xa6.?.?.?.?.?.?.?.?\\xa6|\\xa7.?.?.?.?.?.?.?.?\\xa7|\\xa8.?.?.?.?.?.?.?.?\\xa8|\\xa9.?.?.?.?.?.?.?.?\\xa9|\\xaa.?.?.?.?.?.?.?.?\\xaa|\\xab.?.?.?.?.?.?.?.?\\xab|\\xac.?.?.?.?.?.?.?.?\\xac|\\xad.?.?.?.?.?.?.?.?\\xad\
152|\\xae.?.?.?.?.?.?.?.?\\xae|\\xaf.?.?.?.?.?.?.?.?\\xaf|\\xb0.?.?.?.?.?.?.?.?\\xb0|\\xb1.?.?.?.?.?.?.?.?\\xb1|\\xb2.?.?.?.?.?.?.?.?\\xb2|\\xb3.?.?.?.?.?.?.?.?\\xb3|\\xb4.?.?.?.?.?.?.?.?\\xb4|\\xb5.?.?.?.?.?.?.?.?\\xb5|\\xb6.?.?.?.?.?.?.?.?\\xb6|\\xb7.?.?.?.?.?.?.?.?\\xb7|\\xb8.?.?.?.?.?.?.?.?\\xb8|\\xb9.?.?.?.?.?.?.?.?\\xb9|\\xba.?.?.?.?.?.?.?.?\\xba|\\xbb.?.?.?.?.?.?.?.?\\xbb|\\xbc.?.?.?.?.?.?.?.?\\xbc|\\xbd.?.?.?.?.?.?.?.?\\xbd|\\xbe.?.?.?.?.?.?.?.?\\xbe|\\xbf.?.?.?.?.?.?.?.?\\xbf|\\xc0.?.?.?.?.?.?.?.?\\xc0|\\xc1.?.?.?.?.?.?.?.?\\xc1\
153|\\xc2.?.?.?.?.?.?.?.?\\xc2|\\xc3.?.?.?.?.?.?.?.?\\xc3|\\xc4.?.?.?.?.?.?.?.?\\xc4|\\xc5.?.?.?.?.?.?.?.?\\xc5|\\xc6.?.?.?.?.?.?.?.?\\xc6|\\xc7.?.?.?.?.?.?.?.?\\xc7|\\xc8.?.?.?.?.?.?.?.?\\xc8|\\xc9.?.?.?.?.?.?.?.?\\xc9|\\xca.?.?.?.?.?.?.?.?\\xca|\\xcb.?.?.?.?.?.?.?.?\\xcb|\\xcc.?.?.?.?.?.?.?.?\\xcc|\\xcd.?.?.?.?.?.?.?.?\\xcd|\\xce.?.?.?.?.?.?.?.?\\xce|\\xcf.?.?.?.?.?.?.?.?\\xcf|\\xd0.?.?.?.?.?.?.?.?\\xd0|\\xd1.?.?.?.?.?.?.?.?\\xd1|\\xd2.?.?.?.?.?.?.?.?\\xd2|\\xd3.?.?.?.?.?.?.?.?\\xd3|\\xd4.?.?.?.?.?.?.?.?\\xd4|\\xd5.?.?.?.?.?.?.?.?\\xd5\
154|\\xd6.?.?.?.?.?.?.?.?\\xd6|\\xd7.?.?.?.?.?.?.?.?\\xd7|\\xd8.?.?.?.?.?.?.?.?\\xd8|\\xd9.?.?.?.?.?.?.?.?\\xd9|\\xda.?.?.?.?.?.?.?.?\\xda|\\xdb.?.?.?.?.?.?.?.?\\xdb|\\xdc.?.?.?.?.?.?.?.?\\xdc|\\xdd.?.?.?.?.?.?.?.?\\xdd|\\xde.?.?.?.?.?.?.?.?\\xde|\\xdf.?.?.?.?.?.?.?.?\\xdf|\\xe0.?.?.?.?.?.?.?.?\\xe0|\\xe1.?.?.?.?.?.?.?.?\\xe1|\\xe2.?.?.?.?.?.?.?.?\\xe2|\\xe3.?.?.?.?.?.?.?.?\\xe3|\\xe4.?.?.?.?.?.?.?.?\\xe4|\\xe5.?.?.?.?.?.?.?.?\\xe5|\\xe6.?.?.?.?.?.?.?.?\\xe6|\\xe7.?.?.?.?.?.?.?.?\\xe7|\\xe8.?.?.?.?.?.?.?.?\\xe8|\\xe9.?.?.?.?.?.?.?.?\\xe9\
155|\\xea.?.?.?.?.?.?.?.?\\xea|\\xeb.?.?.?.?.?.?.?.?\\xeb|\\xec.?.?.?.?.?.?.?.?\\xec|\\xed.?.?.?.?.?.?.?.?\\xed|\\xee.?.?.?.?.?.?.?.?\\xee|\\xef.?.?.?.?.?.?.?.?\\xef|\\xf0.?.?.?.?.?.?.?.?\\xf0|\\xf1.?.?.?.?.?.?.?.?\\xf1|\\xf2.?.?.?.?.?.?.?.?\\xf2|\\xf3.?.?.?.?.?.?.?.?\\xf3|\\xf4.?.?.?.?.?.?.?.?\\xf4|\\xf5.?.?.?.?.?.?.?.?\\xf5|\\xf6.?.?.?.?.?.?.?.?\\xf6|\\xf7.?.?.?.?.?.?.?.?\\xf7|\\xf8.?.?.?.?.?.?.?.?\\xf8|\\xf9.?.?.?.?.?.?.?.?\\xf9|\\xfa.?.?.?.?.?.?.?.?\\xfa|\\xfb.?.?.?.?.?.?.?.?\\xfb|\\xfc.?.?.?.?.?.?.?.?\\xfc|\\xfd.?.?.?.?.?.?.?.?\\xfd\
156|\\xfe.?.?.?.?.?.?.?.?\\xfe|\\xff.?.?.?.?.?.?.?.?\\xff)"},
157    {"netgeareva", L7_ENUM_NETGEAREVA, 0, "^...\\x53\\x4a\\x61\\x6d"}, /*foxconn add by pingod, 07/12/2008*/
158    //{"ftp", 7, 0, "^220[\\x09-\\x0d -~]*ftp"},
159    //{"http", 8, 0, "http/(0\\.9|1\\.0|1\\.1) [1-5][0-9][0-9] [\\x09-\\x0d -~]*(connection:|content-type:|content-length:|date:)|post [\\x09-\\x0d -~]* http/[01]\\.[019]"},
160    //{"msnmessenger", 9, 0, "ver [0-9]+ msnp[1-9][0-9]? [\\x09-\\x0d -~]*cvr0\\x0d\\x0a$|usr 1 [!-~]+ [0-9. ]+\\x0d\\x0a$|ans 1 [!-~]+ [0-9. ]+\\x0d\\x0a$"},
161    //{"xunlei", 10, 0, "^[()]...?.?.?(reg|get|query)"},
162    //{"ares", 11, 0, "^\\x03[]Z].?.?\\x05$"},
163};
164
165/* temporary buffer for pre-processing expression data */
166static char g_exp[APP_DATA_BUF_MAX_LEN];
167
168/* temporary buffer for converting app data into lowercase */
169static char g_app_data[APP_DATA_BUF_MAX_LEN];
170
171#ifdef L7_DEBUG_ON
172static int all_packet_in = 0;
173static int all_packet_out = 0;
174static int packet_has_pri_in = 0;
175static int packet_has_pri_out = 0;
176#endif
177
178
179/* Foxconn added start pling 11/08/2007 */
180#define QOS_PRIORITY_HIGHEST        3
181#define QOS_PRIORITY_HIGH           2
182#define QOS_PRIORITY_NORMAL         1
183#define QOS_PRIORITY_LOW            0
184#define QOS_PRIORITY_UNSPECIFIED    -1
185
186static int qos_l7_apps_priority[L7_ENUM_TOTAL + 1]; /* Apps enum starts from 1 to L7_ENUM_TOTAL, 0 is unused */
187
188static void load_apps_priority(void)
189{
190    char qos_l7_appls[1024];
191    char *temp;
192    int  apps, priority, i;
193
194    /* Set default apps priority */
195    for (i=0; i<=L7_ENUM_TOTAL; i++)
196        qos_l7_apps_priority[i] = QOS_PRIORITY_UNSPECIFIED;
197
198    strcpy(qos_l7_appls, nvram_safe_get("qos_l7_apps"));
199
200    temp = strtok(qos_l7_appls, " ");
201    while (temp)
202    {
203        sscanf(temp, "%d:%d", &apps, &priority);
204
205        /* Do Sanity check */
206        if (apps >= 1 && apps <= L7_ENUM_TOTAL &&
207            priority >= QOS_PRIORITY_LOW && priority <= QOS_PRIORITY_HIGHEST)
208        {
209            printk("L7 apps %d, priority %d\n", apps, priority);
210            qos_l7_apps_priority[apps] = priority;
211        }
212
213        temp = strtok(NULL, " ");
214    }
215}
216
217#define QOS_DSCP_VALUE_HIGHEST  (0x38 << 2)
218#define QOS_DSCP_VALUE_HIGH     (0x28 << 2)
219#define QOS_DSCP_VALUE_NORMAL   (0x0  << 2)
220#define QOS_DSCP_VALUE_LOW      (0x10 << 2)
221
222unsigned char prio_to_dscp(unsigned char prio)
223{
224    if (prio == QOS_PRIORITY_HIGHEST)
225        return QOS_DSCP_VALUE_HIGHEST;
226    else
227    if (prio == QOS_PRIORITY_HIGH)
228        return QOS_DSCP_VALUE_HIGH;
229    else
230    if (prio == QOS_PRIORITY_LOW)
231        return QOS_DSCP_VALUE_LOW;
232    else
233        return QOS_DSCP_VALUE_NORMAL;
234}
235
236UINT16 CalcChecksum(unsigned char *pbData, int iLength)
237{
238	int nLeft = iLength % 2; /* this addr has odd byte? */
239    UINT16 wChecksum;
240    int iIndex;
241	UINT32 dwSum = 0;
242
243    for (iIndex=0; iIndex < iLength-1; iIndex+=2)
244    {
245        dwSum += (pbData[iIndex] << 8) | (pbData[iIndex+1]);
246    }
247
248    if (nLeft != 0)
249        dwSum += (pbData[iLength-1] << 8);
250
251
252	dwSum = (dwSum >> 16) + (dwSum & 0xffff);     /* add hi 16 to low 16 */
253	dwSum += (dwSum >> 16);                     /* add carry */
254	wChecksum = ~dwSum;                          /* truncate to 16 bits */
255	return (wChecksum);
256
257}
258
259static int modify_dscp(struct sk_buff *skb, int apps)
260{
261    int pkt_prio;
262    int l7_prio;
263    int i;
264
265    pkt_prio = skb->cb[sizeof(skb->cb) - 3];
266    l7_prio  = qos_l7_apps_priority[apps];
267
268    //printk("#### Got L7apps %d, orig prio %d, l7 prio %d\n", apps, pkt_prio, l7_prio);
269
270    if (l7_prio > pkt_prio)
271    {
272        unsigned char *ipHdr;
273        unsigned int ipHdrLen;
274        UINT16 csum;
275
276#if 0
277        printk("skb->data: ");
278
279        for (i=0; i<20; i++)
280            printk("%02X ", (unsigned char)skb->data[i]);
281        printk("\n");
282#endif
283        /* Get start of IP header & TCP header */
284        ipHdr = &(skb->data[0]);
285        ipHdrLen = (ipHdr[0] & 0x0F) * 4;
286
287        /* Modify the DSCP value */
288        ipHdr[1] = prio_to_dscp(l7_prio);
289
290        /* Recompute TCP checksum */
291        ipHdr[10] = 0;
292        ipHdr[11] = 0;
293        csum = CalcChecksum(ipHdr, ipHdrLen);
294        ipHdr[10] = (csum & 0xFF00) >> 8;
295        ipHdr[11] = (csum & 0xFF);
296
297       // printk("!!!!!! Promote L7apps %d prio from %d to %d\n", apps, pkt_prio, l7_prio);
298    }
299    return 0;
300
301}
302/* Foxconn added end pling 11/08/2007 */
303
304/*******function start*******/
305
306unsigned char layer7_scan_all(struct sk_buff *skb, char *app_data, int appdatalen)
307{
308    s_pattern_cache *node = first_pattern_cache;
309    int str_len = 0;
310    int i = 0;
311
312#ifdef L7_DEBUG_ON
313    L7_PRINTK_DEBUG_3("layer7_scan_all start\n");
314#endif
315
316    str_len = ((sizeof(g_app_data)-1) > appdatalen) ? appdatalen : (sizeof(g_app_data)-1);
317    for (i=0; i<str_len; i++)
318        g_app_data[i] = tolower(app_data[i]);
319    g_app_data[i] = 0;
320
321    while (node != NULL)
322    {
323        if (regexec(node->pattern, g_app_data))
324        {
325#ifdef L7_DEBUG_ON
326            L7_PRINTK_DEBUG_7("layer7 filter match: expression: %s\n", node->regex_string);
327            L7_PRINTK_DEBUG_1("layer7 filter match: protocol: %s\n", node->proto_name);
328#endif
329            return node->proto_enum;
330        }
331
332        node = node->next;
333    }
334    return 0;//dismatch
335}
336/* Returns offset the into the skb->data that the application data starts */
337static int app_data_offset(const struct sk_buff *skb)
338{
339	/* In case we are ported somewhere (ebtables?) where skb->nh.iph
340	isn't set, this can be gotten from 4*(skb->data[0] & 0x0f) as well. */
341/* Fxcn port-S Wins, 0717-09 */
342#ifdef LINUX26
343    int ip_hl = 4 * ip_hdr(skb)->ihl;
344#else /* LINUX26 */
345/* Fxcn port-E Wins, 0717-09 */
346	int ip_hl = 4*skb->nh.iph->ihl;
347/* Fxcn port-S Wins, 0717-09 */
348#endif
349/* Fxcn port-E Wins, 0717-09 */
350
351/* Fxcn port-S Wins, 0717-09 */
352#ifdef LINUX26
353	if ( ip_hdr(skb)->protocol == IPPROTO_TCP )
354#else /* LINUX26 */
355/* Fxcn port-E Wins, 0717-09 */
356	if ( skb->nh.iph->protocol == IPPROTO_TCP )
357/* Fxcn port-S Wins, 0717-09 */
358#endif /* LINUX26 */
359/* Fxcn port-E Wins, 0717-09 */
360	{
361		/* 12 == offset into TCP header for the header length field.
362		Can't get this with skb->h.th->doff because the tcphdr
363		struct doesn't get set when routing (this is confirmed to be
364		true in Netfilter as well as QoS.) */
365		int tcp_hl = 4*(skb->data[ip_hl + 12] >> 4);
366
367		return ip_hl + tcp_hl;
368	}
369/* Fxcn port-S Wins, 0717-09 */
370#ifdef LINUX26
371	else if ( ip_hdr(skb)->protocol == IPPROTO_UDP  )
372#else /* LINUX26 */
373/* Fxcn port-E Wins, 0717-09 */
374	else if ( skb->nh.iph->protocol == IPPROTO_UDP  )
375/* Fxcn port-S Wins, 0717-09 */
376#endif /* LINUX26 */
377/* Fxcn port-E Wins, 0717-09 */
378	{
379		return ip_hl + 8; /* UDP header is always 8 bytes */
380	}
381/* Fxcn port-S Wins, 0717-09 */
382#ifdef LINUX26
383	else if ( ip_hdr(skb)->protocol == IPPROTO_ICMP )
384#else /* LINUX26 */
385/* Fxcn port-E Wins, 0717-09 */
386	else if ( skb->nh.iph->protocol == IPPROTO_ICMP )
387/* Fxcn port-S Wins, 0717-09 */
388#endif /* LINUX26 */
389/* Fxcn port-E Wins, 0717-09 */
390	{
391		return ip_hl + 8; /* ICMP header is 8 bytes */
392	}
393	else
394	{
395		if (net_ratelimit())
396			L7_PRINTK_DEBUG_3(KERN_ERR "layer7: tried to handle unknown protocol!\n");
397		return ip_hl + 8; /* something reasonable */
398	}
399}
400
401int l7_filter_main(struct sk_buff *skb, int iDirection)
402{
403    unsigned char   *app_data;
404    unsigned int    appdatalen;
405    T_ConnEntry     *pConn;
406    unsigned char   proto_enum = 0;
407    int             iModifyType = 0;
408#ifdef L7_DEBUG_ON
409    int             i = 0;
410    int             iLen = sizeof(g_all_proto)/sizeof(g_all_proto[0]);
411#endif
412
413#ifdef L7_DEBUG_ON
414    L7_PRINTK_DEBUG_2("l7_filter_main start skb: %x, all_in out: %d %d, has_pri_in out: %d %d, in or out: %d\n",
415        skb, all_packet_in, all_packet_out, packet_has_pri_in, packet_has_pri_out, iDirection);
416    if (0 == ((all_packet_out+all_packet_in)&0xff))//print per 256 packets
417        L7_PRINTK_DEBUG_1("l7_filter_main start per 256, skb: %x, all_in out: %d %d, has_pri_in out: %d %d, in or out: %d\n",
418            skb, all_packet_in, all_packet_out, packet_has_pri_in, packet_has_pri_out, iDirection);
419    if (0 == ((all_packet_out+all_packet_in)&0x03ff))//list identified packets status of every proto per 1024 packets
420    {
421        L7_PRINTK_DEBUG_1("*********************** start ************************\n");
422        L7_PRINTK_DEBUG_1("l7_filter_main per 1024, all_out: %d, has_pri_out: %d\n",
423            all_packet_out, packet_has_pri_out);
424        for (i=0; i<iLen; i++)
425            L7_PRINTK_DEBUG_1("Proto %s identified %d packets\n", g_all_proto[i].proto_name, g_all_proto[i].proto_packet_count);
426        L7_PRINTK_DEBUG_1("************************ end *************************\n");
427    }
428    if (iDirection)
429        all_packet_out++;
430    else
431        all_packet_in++;
432#endif
433
434    if (NULL == skb)
435        return L7_ERROR;
436
437    skb->cb[sizeof(skb->cb)-4] = L7_ENUM_INIT;//initial value: -1(0xff)
438
439/* Fxcn port-S Wins, 0717-09 */
440#ifdef LINUX26
441    pConn = FindConnByHash(ip_hdr(skb), &iModifyType, iDirection);
442#else /* LINUX26 */
443/* Fxcn port-E Wins, 0717-09 */
444    pConn = FindConnByHash(skb->nh.iph, &iModifyType, iDirection);
445/* Fxcn port-S Wins, 0717-09 */
446#endif /* LINUX26 */
447/* Fxcn port-E Wins, 0717-09 */
448
449    if (NULL == pConn)
450    {
451#ifdef L7_DEBUG_ON
452        if (iDirection)
453            L7_PRINTK_DEBUG_2("l7_filter_main FindConnByHash fail.\n");
454#endif
455        return L7_SUCCESS;
456    }
457
458#ifdef L7_DEBUG_ON
459    L7_PRINTK_DEBUG_3("l7_filter_main pConn: %x, pConn->packet_count: %d\n", pConn, pConn->packet_count);
460#endif
461    if (pConn->proto_enum != 0)
462    {
463        if (iDirection)
464        {
465#ifdef L7_DEBUG_ON
466            packet_has_pri_out++;
467            g_all_proto[pConn->proto_enum-1].proto_packet_count++;
468#endif
469            skb->cb[sizeof(skb->cb)-4] = pConn->proto_enum;
470            if (skb->cb[sizeof(skb->cb)-5])
471                modify_dscp(skb, pConn->proto_enum);
472        }
473#ifdef L7_DEBUG_ON
474        else
475            packet_has_pri_in++;
476        L7_PRINTK_DEBUG_3("l7_filter_main pConn->proto_enum: %d\n", pConn->proto_enum);
477#endif
478        return L7_SUCCESS;
479    }
480
481    if (pConn->packet_count > num_packets)
482    {
483#ifdef L7_DEBUG_ON
484        L7_PRINTK_DEBUG_3("too many packets, l7_filter_main pConn->packet_count: %d\n", pConn->packet_count);
485#endif
486        if (iDirection)
487        {
488            skb->cb[sizeof(skb->cb)-4] = pConn->proto_enum;
489            if (skb->cb[sizeof(skb->cb)-5])
490                modify_dscp(skb, pConn->proto_enum);
491        }
492
493        return L7_SUCCESS;
494    }
495    pConn->packet_count++;
496
497    if (skb_is_nonlinear(skb))
498    {
499/* Fxcn port-S Wins, 0717-09 */
500#ifdef LINUX26
501        if (skb_linearize(skb) != 0)
502#else /* LINUX26 */
503/* Fxcn port-E Wins, 0717-09 */
504        if (skb_linearize(skb, GFP_ATOMIC) != 0)
505/* Fxcn port-S Wins, 0717-09 */
506#endif /* LINUX26 */
507/* Fxcn port-E Wins, 0717-09 */
508        {
509            if (net_ratelimit())
510                L7_PRINTK_DEBUG_1(KERN_ERR "layer7: failed to linearize packet, bailing.\n");
511            return L7_SUCCESS;
512        }
513    }
514
515    /* now that the skb is linearized, it's safe to set these. */
516    app_data = skb->data + app_data_offset(skb);
517    appdatalen = skb->tail - app_data;
518
519    proto_enum = layer7_scan_all(skb, app_data, appdatalen);
520
521    if (proto_enum == L7_ENUM_NETGEAREVA)
522    {
523        if ((pConn->bProtocol != UDP_PROTOCOL)
524            || ((pConn->bNatType != AG_PORT_FORWARDING)
525                  && ((ntohs(pConn->wDestPort) < EVA_PORT_START) || (ntohs(pConn->wDestPort) > EVA_PORT_END)))
526            || ((pConn->bNatType == AG_PORT_FORWARDING)
527                  && ((ntohs(pConn->wSourcePort) < EVA_PORT_START) || (ntohs(pConn->wSourcePort) > EVA_PORT_END))))
528        {
529            proto_enum = 0;
530        }
531    }
532
533    if (proto_enum != 0)
534    {
535#ifdef L7_DEBUG_ON
536        L7_PRINTK_DEBUG_1("l7_filter_main identify the conn %x, in %dth packet.\n", pConn, pConn->packet_count);
537#endif
538        if (iDirection)
539        {
540#ifdef L7_DEBUG_ON
541            packet_has_pri_out++;
542            g_all_proto[proto_enum-1].proto_packet_count++;
543#endif
544            skb->cb[sizeof(skb->cb)-4] = proto_enum;
545            if (skb->cb[sizeof(skb->cb)-5])
546                modify_dscp(skb, pConn->proto_enum);
547        }
548#ifdef L7_DEBUG_ON
549        else
550            packet_has_pri_in++;
551#endif
552        pConn->proto_enum = proto_enum;
553        return L7_SUCCESS;
554    }
555    return L7_SUCCESS;
556}
557
558static int isxdigit(char c)
559{
560    if ((c >= '0' && c <= '9')
561        || (c >= 'a' && c <= 'f')
562        || (c >= 'A' && c <= 'F'))
563        return 1;
564    else
565        return 0;
566}
567
568static char tolower(char c)
569{
570    if (c >= 'A' && c <= 'Z')
571        c += 0x20;
572    return c;
573}
574
575static int hex2dec(char c)
576{
577    switch (c)
578    {
579        case '0' ... '9':
580            return c - '0';
581        case 'a' ... 'f':
582            return c - 'a' + 10;
583        case 'A' ... 'F':
584            return c - 'A' + 10;
585        default:
586#ifdef L7_DEBUG_ON
587            L7_PRINTK_DEBUG_2("hex2dec: bad value!\n");
588#endif
589            return 0;
590    }
591}
592
593int pre_process(char *str_out, char *str_in, int str_out_len)
594{
595    char *result = str_out;
596    char *s = str_in;
597    int sindex = 0, rindex = 0;
598
599    while ( sindex < strlen(s) && (rindex + 1 < str_out_len))
600    {
601        if ( sindex + 3 < strlen(s) &&
602            s[sindex] == '\\' && s[sindex+1] == 'x' &&
603            isxdigit(s[sindex + 2]) && isxdigit(s[sindex + 3]) )
604        {
605            /* carefully remember to call tolower here... */
606            result[rindex] = tolower( hex2dec(s[sindex + 2])*16 +
607                                      hex2dec(s[sindex + 3] ) );
608
609            switch ( result[rindex] )
610            {
611                case 0x24:
612                case 0x28:
613                case 0x29:
614                case 0x2a:
615                case 0x2b:
616                case 0x2e:
617                case 0x3f:
618                case 0x5b:
619                case 0x5c:
620                case 0x5d:
621                case 0x5e:
622                case 0x7c:
623#ifdef L7_DEBUG_ON
624                    L7_PRINTK_DEBUG_1("Warning: layer7 regexp contains a control character, %c, in hex (\\x%c%c).\n"
625                        "I recommend that you write this as %c or \\%c, depending on what you meant.\n",
626                        result[rindex], s[sindex + 2], s[sindex + 3], result[rindex], result[rindex]);
627#endif
628                    break;
629                case 0x00:
630#ifdef L7_DEBUG_ON
631                    L7_PRINTK_DEBUG_1("Warning: null (\\x00) in layer7 regexp.  A null terminates the regexp string!\n");
632#endif
633                    break;
634                default:
635                    break;
636            }
637
638            sindex += 3; /* 4 total */
639        }
640        else
641            result[rindex] = tolower(s[sindex]);
642
643        sindex++;
644        rindex++;
645    }
646    result[rindex] = '\0';
647
648    if (rindex > 0)
649        return 0;
650    else
651        return 1;
652}
653
654int load_all_pattern(void)
655{
656    /* establish the link list of patterns*/
657    s_pattern_cache * node = first_pattern_cache;
658    s_pattern_cache * tmp;
659    unsigned int len;
660    unsigned int num_proto = sizeof(g_all_proto)/sizeof(s_proto_regexp);
661    int i = 0;
662
663#ifdef L7_DEBUG_ON
664    L7_PRINTK_DEBUG_1("l7 filter, load_all_pattern start, num_proto: %d\n", num_proto);
665#endif
666
667    for (i=0; i<num_proto; i++)
668    {
669#ifdef L7_DEBUG_ON
670        L7_PRINTK_DEBUG_2("load_all_pattern for loop: %d, proto_regexp: %s,\n
671            sizeof(s_pattern_cache): %d.\n",
672            i, g_all_proto[i].proto_regexp, sizeof(s_pattern_cache));
673#endif
674        if (0 != pre_process(g_exp, g_all_proto[i].proto_regexp, sizeof(g_exp)))
675            continue;
676
677        /* Be paranoid about running out of memory to avoid list corruption. */
678        tmp = kmalloc(sizeof(s_pattern_cache), GFP_ATOMIC);
679        if (!tmp)
680        {
681            L7_PRINTK_DEBUG_1("layer7: out of memory in compile_and_cache, bailing. 1\n");
682            if (net_ratelimit())
683                L7_PRINTK_DEBUG_1(KERN_ERR "layer7: out of memory in compile_and_cache, bailing.\n");
684            return NULL;
685        }
686
687        tmp->regex_string   = kmalloc(strlen(g_exp) + 1, GFP_ATOMIC);
688        if (NULL == tmp->regex_string)
689        {
690            L7_PRINTK_DEBUG_1("layer7: out of memory in compile_and_cache, bailing. 2\n");
691            if (net_ratelimit())
692                L7_PRINTK_DEBUG_1(KERN_ERR "layer7: out of memory in compile_and_cache, bailing.\n");
693            kfree(tmp);
694            return NULL;
695        }
696
697        tmp->pattern        = regcomp(g_exp, &len);
698        if (NULL == tmp->pattern)
699        {
700            L7_PRINTK_DEBUG_1("layer7: out of memory in compile_and_cache, bailing. 3\n");
701            if (net_ratelimit())
702                L7_PRINTK_DEBUG_1(KERN_ERR "layer7: out of memory in compile_and_cache, bailing.\n");
703            kfree(tmp->regex_string);
704            kfree(tmp);
705            return NULL;
706        }
707
708        strcpy(tmp->regex_string, g_exp);
709        tmp->next           = NULL;
710        tmp->proto_enum     = g_all_proto[i].proto_enum;
711        tmp->proto_name     = g_all_proto[i].proto_name;
712
713#ifdef L7_DEBUG_ON
714        L7_PRINTK_DEBUG_2("regexp: %s\n", tmp->proto_name);
715        L7_PRINTK_DEBUG_2("tmp->pattern->regstart: %d\n", tmp->pattern->regstart);
716        L7_PRINTK_DEBUG_2("tmp->pattern->reganch: %d\n", tmp->pattern->reganch);
717        if (tmp->pattern->regmust != NULL)
718            L7_PRINTK_DEBUG_2("tmp->pattern->regmust: %s\n", tmp->pattern->regmust);
719        L7_PRINTK_DEBUG_2("tmp->pattern->regmlen: %d\n", tmp->pattern->regmlen);
720        L7_PRINTK_DEBUG_2("tmp->pattern->program[0]: %d\n", tmp->pattern->program[0]);
721#endif/* end of #ifdef L7_DEBUG_ON */
722        if (NULL == node)
723        {
724            node = tmp;
725            first_pattern_cache = tmp;
726        }
727        else
728        {
729            node->next = tmp;
730            node = tmp;
731        }
732    }
733
734    return 1;
735}
736
737void free_all_pattern(void)
738{
739    s_pattern_cache *node = first_pattern_cache;
740    s_pattern_cache *tmp;
741
742    while (node != NULL)
743    {
744        if (node->regex_string != NULL)
745            kfree(node->regex_string);
746        if (node->pattern != NULL)
747            kfree(node->pattern);
748
749        tmp = node;
750        node = node->next;
751        kfree(tmp);
752    }
753}
754
755
756
757static int __init init(void)
758{
759    int ret = 0;
760
761#ifdef CONFIG_NETFILTER
762    ret = nf_register_hook(&l7_filter_prerouting_ops);
763    if (ret < 0) {
764        printk("<0>Error registering l7 filter prerouting.\n");
765        nf_unregister_hook(&l7_filter_prerouting_ops);
766        return ret;
767    }
768    ret = nf_register_hook(&l7_filter_postrouting_ops);
769    if (ret < 0) {
770        printk("<0>Error registering l7 filter postrouting.\n");
771        return ret;
772    }
773#else /* CONFIG_NETFILTER */
774    acosL7Filter_register_hook(l7_filter_main);
775#endif /* CONFIG_NETFILTER */
776
777    load_all_pattern();
778
779    load_apps_priority();       /* Foxconn added pling 11/08/2007 */
780
781    return ret;
782}
783
784static void __exit fini(void)
785{
786#ifdef CONFIG_NETFILTER
787    nf_unregister_hook(&l7_filter_prerouting_ops);
788    nf_unregister_hook(&l7_filter_postrouting_ops);
789#else
790    acosL7Filter_register_hook(NULL);
791#endif /* CONFIG_NETFILTER */
792
793    free_all_pattern();
794}
795
796module_init(init);
797module_exit(fini);
798
799EXPORT_SYMBOL(l7_filter_main);
800