12 files changed, 164 insertions, 152 deletions
diff --git a/noncore/net/wellenreiter/libwellenreiter/source/Makefile b/noncore/net/wellenreiter/libwellenreiter/source/Makefile index 3670d96..51b5ff3 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/Makefile +++ b/noncore/net/wellenreiter/libwellenreiter/source/Makefile @@ -1,17 +1,17 @@ # $Id$ -INCLUDES = -LIBRARIES = -LIBOBJ = proto.o sock.o log.o cardmode.o sniff.o -CXX = g++ -Wall -pedantic -g $(INCLUDES) -DDEBUG +OBJ = wl_proto.o wl_sock.o wl_log.o cardmode.o sniff.o +CPP = g++ +CPPFLAGS = -Wall -pedantic -g -DDEBUG static: libwellenreiter.a -libwellenreiter.a: $(LIBOBJ) - ar -cr libwellenreiter.a $(LIBOBJ) +libwellenreiter.a: $(OBJ) + ar -cr libwellenreiter.a $(OBJ) shared: libwellenreiter.so -libwellenreiter.so: $(LIBOBJ) - $(CXX) -shared -o libwellenreiter.so $(LIBOBJ) +libwellenreiter.so: $(OBJ) + $(CPP) $(CPPFLAGS) -shared -o libwellenreiter.so $(OBJ) clean: rm -f *.o *.a *.so *~ + diff --git a/noncore/net/wellenreiter/libwellenreiter/source/cardmode.cc b/noncore/net/wellenreiter/libwellenreiter/source/cardmode.cc index 3aaa4a7..c29f4d0 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/cardmode.cc +++ b/noncore/net/wellenreiter/libwellenreiter/source/cardmode.cc @@ -1,78 +1,77 @@ /* * Set card modes for sniffing * * $Id$ */ #include "cardmode.hh" -#include "log.hh" +#include "wl_log.hh" /* main card into monitor function */ int card_into_monitormode (pcap_t **orighandle, char *device, int cardtype) { char CiscoRFMON[35] = "/proc/driver/aironet/"; FILE *CISCO_CONFIG_FILE; - pcap_t *handle = (pcap_t*)orighandle; /* Checks if we have a device to sniff on */ if(device == NULL) { wl_logerr("No device given"); return 0; } /* Setting the prmiscous and up flag to the interface */ if (!card_set_promisc_up(device)) { wl_logerr("Cannot set interface to promisc mode: %s", strerror(errno)); return 0; } wl_loginfo("Interface set to promisc mode"); /* Check the cardtype and executes the commands to go into monitor mode */ if (cardtype == CARD_TYPE_CISCO) { /* bring the sniffer into rfmon mode */ - snprintf(CiscoRFMON, sizeof(CiscoRFMON), DEFAULT_PATH, device); + snprintf(CiscoRFMON, sizeof(CiscoRFMON) - 1, DEFAULT_PATH, device); if((CISCO_CONFIG_FILE = fopen(CiscoRFMON,"w")) == NULL) { wl_logerr("Cannot open config file: %s", strerror(errno)); return 0; } fputs ("Mode: r",CISCO_CONFIG_FILE); fputs ("Mode: y",CISCO_CONFIG_FILE); fputs ("XmitPower: 1",CISCO_CONFIG_FILE); fclose(CISCO_CONFIG_FILE); } else if (cardtype == CARD_TYPE_NG) { char wlanngcmd[62]; - snprintf(wlanngcmd, sizeof(wlanngcmd), "%s %s lnxreq_wlansniff channel=1 enable=true", WLANCTL_PATH, device); + snprintf(wlanngcmd, sizeof(wlanngcmd) - 1, "%s %s lnxreq_wlansniff channel=1 enable=true", WLANCTL_PATH, device); if (system(wlanngcmd) != 0) { wl_logerr("Could not set %s in raw mode, check cardtype", device); return 0; } } else if (cardtype == CARD_TYPE_HOSTAP) { wl_logerr("Got a host-ap card, nothing is implemented now"); } return 1; } /* Set card into promisc mode */ int card_set_promisc_up (const char *device) { char ifconfigcmd[32]; int retval=0; - snprintf(ifconfigcmd, sizeof(ifconfigcmd), SBIN_PATH, device); + snprintf(ifconfigcmd, sizeof(ifconfigcmd) - 1, SBIN_PATH, device); retval = system(ifconfigcmd); if(retval != 0) return 0; return 1; } diff --git a/noncore/net/wellenreiter/libwellenreiter/source/cardmode.hh b/noncore/net/wellenreiter/libwellenreiter/source/cardmode.hh index b85b7b7..6ec5b95 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/cardmode.hh +++ b/noncore/net/wellenreiter/libwellenreiter/source/cardmode.hh @@ -1,36 +1,32 @@ /* $Id$ */ #ifndef CARDMODE_HH #define CARDMODE_HH #include <string.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> - -extern "C" -{ #include <net/bpf.h> #include <pcap.h> -} /* Defines, used for the card setup */ #define DEFAULT_PATH "/proc/driver/aironet/%s/Config" #define CARD_TYPE_CISCO 1 #define CARD_TYPE_NG 2 #define CARD_TYPE_HOSTAP 3 /* only for now, until we have the daemon running */ /*the config file should provide these information */ #define CARD_TYPE CARD_TYPE_HOSTAP #define SBIN_PATH "/sbin/ifconfig %s promisc up" #define WLANCTL_PATH "/sbin/wlanctl-ng" /* Prototypes */ int card_into_monitormode (pcap_t **, char *, int); int card_set_promisc_up (const char *); #endif /* CARDMODE_HH */ diff --git a/noncore/net/wellenreiter/libwellenreiter/source/proto.cc b/noncore/net/wellenreiter/libwellenreiter/source/proto.cc deleted file mode 100644 index a1228e4..0000000 --- a/noncore/net/wellenreiter/libwellenreiter/source/proto.cc +++ b/dev/null @@ -1,109 +0,0 @@ -/* - * Communication protocol - * - * $Id$ - */ - -#include "proto.hh" -#include "sock.hh" -#include "log.hh" - -/* Send found network to GUI */ -int send_network_found (const char *guihost, int guiport, void *structure) -{ - wl_network_t *ptr; - char buffer[2048]; - char temp[5]; - - ptr = (wl_network_t *)structure; - - memset(buffer, 0, 2048); - - /* Type = Found new net */ - memcpy(buffer, "01", 2); - - /* Set Net-type */ - memset(temp, 0, sizeof(temp)); - snprintf(temp, 2, "%d", ptr->net_type); - memcpy(buffer + 2, temp, 1); - - /* Set channel */ - memset(temp, 0, sizeof(temp)); - - if(ptr->channel < 10) - snprintf(temp, 3, "0%d", ptr->channel); - else - snprintf(temp, 3, "%d", ptr->channel); - - memcpy(buffer + 3, temp, 2); - - /* Set WEP y/n */ - memset(temp, 0, sizeof(temp)); - snprintf(temp, 2, "%d", ptr->wep); - memcpy(buffer + 5, temp, 1); - - /* Set MAC address */ - memcpy(buffer + 6, ptr->mac, 17); - - /* Set lenght of ssid */ - memset(temp, 0, sizeof(temp)); - - if(ptr->ssid_len > 99) - snprintf(temp, 4, "%d", ptr->ssid_len); - else if(ptr->ssid_len < 10) - snprintf(temp, 4, "00%d", ptr->ssid_len); - else - snprintf(temp, 4, "0%d", ptr->ssid_len); - - memcpy(buffer + 23, temp, 3); - - /* Set ssid */ - memcpy(buffer + 26, ptr->bssid, ptr->ssid_len); - - /* Send prepared buffer to GUI */ -#ifdef DEBUG - wl_loginfo("Sending network to GUI: '%s'", buffer); -#endif - - sendcomm(guihost, guiport, buffer); - - return 1; -} - -/* Fill buffer into structur */ -int get_network_found (void *structure, const char *buffer) -{ - wl_network_t *ptr; - char temp[512]; - - ptr = (wl_network_t *)structure; - - /* Get net type */ - memset(temp, 0, sizeof(temp)); - memcpy(temp, buffer + 2, 1); - ptr->net_type = atoi(temp); - - /* Get channel */ - memset(temp, 0, sizeof(temp)); - memcpy(temp, buffer + 3, 2); - ptr->channel = atoi(temp); - - /* Set WEP y/n */ - memset(temp, 0, sizeof(temp)); - memcpy(temp, buffer + 5, 1); - ptr->wep = atoi(temp); - - /* Set MAC address */ - memcpy(ptr->mac, buffer + 6, 17); - ptr->mac[17]='\0'; - - /* Set lenght of ssid */ - memset(temp, 0, sizeof(temp)); - memcpy(temp, buffer + 23, 3); - ptr->ssid_len = atoi(temp); - - /* Set ssid */ - memcpy(ptr->bssid, buffer + 26, ptr->ssid_len + 1); - - return 1; -} diff --git a/noncore/net/wellenreiter/libwellenreiter/source/sniff.cc b/noncore/net/wellenreiter/libwellenreiter/source/sniff.cc index c703052..e2169ca 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/sniff.cc +++ b/noncore/net/wellenreiter/libwellenreiter/source/sniff.cc @@ -1,75 +1,75 @@ /* * rfmon mode sniffer * * $Id$ */ #include "sniff.hh" #include "ieee802_11.hh" #include "extract.hh" -#include "log.hh" -#include "proto.hh" +#include "wl_log.hh" +#include "wl_proto.hh" /* Main function, checks packets */ void process_packets(const struct pcap_pkthdr *pkthdr, const unsigned char *packet, char *guihost, int guiport) { unsigned int caplen = pkthdr->caplen; unsigned int length = pkthdr->len; u_int16_t fc; unsigned int HEADER_LENGTH; /* pinfo holds all interresting information for us */ struct packetinfo pinfo; struct packetinfo *pinfoptr; /* wl_network_t will finally be set and send to the ui */ wl_network_t wl_net; pinfoptr=&pinfo; pinfoptr->isvalid = 0; pinfoptr->pktlen = pkthdr->len; if (caplen < IEEE802_11_FC_LEN) { /* This is a garbage packet, because is does not long enough to hold a 802.11b header */ pinfoptr->isvalid = 0; return; } /* Gets the framecontrol bits (2bytes long) */ fc = EXTRACT_LE_16BITS(packet); HEADER_LENGTH = GetHeaderLength(fc); if (caplen < HEADER_LENGTH) { /* This is a garbage packet, because it is not long enough to hold a correct header of its type */ pinfoptr->isvalid = 0; return; } /* Decode 802.11b header out of the packet */ if (decode_80211b_hdr(packet,pinfoptr) == 0) { /* Justification of the ofset to further process the packet */ length -= HEADER_LENGTH; caplen -= HEADER_LENGTH; packet += HEADER_LENGTH; } else /* Something is wrong,could not be a correct packet */ return; switch (FC_TYPE(fc)) { /* Is it a managemnet frame? */ case T_MGMT: switch (FC_SUBTYPE(fc)) { case ST_BEACON: if (handle_beacon(fc, packet,pinfoptr) ==0) { if (!strcmp(pinfoptr->desthwaddr,"ff:ff:ff:ff:ff:ff") == 0) { diff --git a/noncore/net/wellenreiter/libwellenreiter/source/sniff.hh b/noncore/net/wellenreiter/libwellenreiter/source/sniff.hh index c7108ac..713b383 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/sniff.hh +++ b/noncore/net/wellenreiter/libwellenreiter/source/sniff.hh @@ -1,70 +1,66 @@ /* $Id$ */ #ifndef SNIFF_HH #define SNIFF_HH #include <string.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/time.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> - -extern "C" -{ #include <net/bpf.h> #include <pcap.h> -} #define NONBROADCASTING "non-broadcasting" /* holds all the interresting data */ struct packetinfo { int isvalid; int pktlen; int fctype; int fcsubtype; int fc_wep; int cap_WEP; int cap_IBSS; int cap_ESS; int channel; char bssid[sizeof("00:00:00:00:00:00") + 1]; char desthwaddr[sizeof("00:00:00:00:00:00") + 1]; char sndhwaddr[sizeof("00:00:00:00:00:00") + 1]; char ssid[128]; int ssid_len; }; void process_packets(const struct pcap_pkthdr* pkthdr,const u_char* packet, char *, int); int decode_80211b_hdr(const u_char *p,struct packetinfo *ppinfo); void etheraddr_string(register const u_char *ep,char * text); int handle_beacon(u_int16_t fc, const u_char *p,struct packetinfo *ppinfo); int GetHeaderLength(u_int16_t fc); /* * True if "l" bytes of "var" were captured. * * The "snapend - (l) <= snapend" checks to make sure "l" isn't so large * that "snapend - (l)" underflows. * * The check is for <= rather than < because "l" might be 0. */ #define TTEST2(var, l) (snapend - (l) <= snapend && \ (const u_char *)&(var) <= snapend - (l)) /* True if "var" was captured */ #define TTEST(var) TTEST2(var, sizeof(var)) /* Bail if "l" bytes of "var" were not captured */ #define TCHECK2(var, l) if (!TTEST2(var, l)) goto trunc /* Bail if "var" was not captured */ #define TCHECK(var) TCHECK2(var, sizeof(var)) #endif /* SNIFF_HH */ diff --git a/noncore/net/wellenreiter/libwellenreiter/source/log.cc b/noncore/net/wellenreiter/libwellenreiter/source/wl_log.cc index af7f909..7adaba8 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/log.cc +++ b/noncore/net/wellenreiter/libwellenreiter/source/wl_log.cc @@ -1,51 +1,51 @@ /* * Small functions to log to syslog * * $Id$ */ -#include "log.hh" +#include "wl_log.hh" /* Log to syslog INFO */ void wl_loginfo(const char *fmt,...) { char buffer[4096]; va_list ap; memset(buffer, 0, sizeof(buffer)); va_start(ap, fmt); vsnprintf(buffer, sizeof(buffer)-1, fmt, ap); va_end(ap); openlog("libwellenreiter", LOG_PID, LOG_SYSLOG); syslog(LOG_INFO, "(info) %s", buffer); closelog(); #ifdef DEBUG fprintf(stderr, "(info) %s\n", buffer); #endif } /* Log to syslog ERR */ void wl_logerr(const char *fmt,...) { char buffer[4096]; va_list ap; memset(buffer, 0, sizeof(buffer)); va_start(ap, fmt); vsnprintf(buffer, sizeof(buffer)-1, fmt, ap); va_end(ap); openlog("libwellenreiter", LOG_PID, LOG_SYSLOG); syslog(LOG_INFO, "(err) %s", buffer); closelog(); #ifdef DEBUG fprintf(stderr, "(err) %s\n", buffer); #endif } diff --git a/noncore/net/wellenreiter/libwellenreiter/source/log.hh b/noncore/net/wellenreiter/libwellenreiter/source/wl_log.hh index 8f6e543..8f6e543 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/log.hh +++ b/noncore/net/wellenreiter/libwellenreiter/source/wl_log.hh diff --git a/noncore/net/wellenreiter/libwellenreiter/source/wl_proto.cc b/noncore/net/wellenreiter/libwellenreiter/source/wl_proto.cc new file mode 100644 index 0000000..f15523f --- a/dev/null +++ b/noncore/net/wellenreiter/libwellenreiter/source/wl_proto.cc @@ -0,0 +1,132 @@ +/* + * Communication protocol + * + * $Id$ + */ + +#include "wl_proto.hh" +#include "wl_log.hh" +#include "wl_sock.hh" + +/* Adds a field to the buffer */ +int add_field(char *buffer, char *string, int len) +{ + char newlen[5]; + + /* 3 Byte = Length */ + snprintf(newlen, sizeof(newlen) - 1, "%.3d", len); + memcpy(buffer, newlen, 3); + + /* Length bytes = Value */ + memcpy(buffer + 3, string, atoi(newlen)); + + /* Return length of attached field */ + return (atoi(newlen) + 3); +} + +int get_field(const char *buffer, char *out) +{ + char len[5]; + + /* Get length of value */ + memcpy(len, buffer, 3); + + /* Copy buffer to out pointer */ + memset(out, 0, atoi(len) + 1); + memcpy(out, buffer + 3, atoi(len)); + + return atoi(len) + 3; +} + +/* Send found network to GUI */ +int send_network_found (const char *guihost, int guiport, void *structure) +{ + wl_network_t *ptr; + char buffer[2048]; + char temp[128]; + int retval=0, len=0; + + memset(temp, 0, sizeof(temp)); + + ptr = (wl_network_t *)structure; + + /* Type = Found new net (without length field) */ + memset(temp, 0, sizeof(temp)); + snprintf(temp, sizeof(temp), "%.2d", NETFOUND); + memcpy(buffer, temp, 2); + len += 2; + + /* Set Net-type */ + memset(temp, 0, sizeof(temp)); + snprintf(temp, sizeof(temp), "%d", ptr->net_type); + retval = add_field(buffer + len, temp, 1); + len += retval; + + /* Set channel */ + memset(temp, 0, sizeof(temp)); + snprintf(temp, sizeof(temp), "%.2d", ptr->channel); + retval = add_field(buffer + len, temp, 2); + len += retval; + + /* Set WEP y/n */ + memset(temp, 0, sizeof(temp)); + snprintf(temp, sizeof(temp), "%d", ptr->wep); + retval = add_field(buffer + len, temp, 1); + len += retval; + + /* Set Mac */ + retval = add_field(buffer + len, (char *)ptr->mac, 17); + len += retval; + + /* Set ssid */ + retval = add_field(buffer + len, (char *)ptr->bssid, ptr->ssid_len); + + /* Send prepared buffer to GUI */ +#ifdef DEBUG + wl_loginfo("Sending network to GUI: '%s'", buffer); +#endif + + wl_send(guihost, guiport, buffer); + + return 1; +} + +/* Fill buffer into structur */ +int get_network_found (void *structure, const char *buffer) +{ + wl_network_t *ptr; + char temp[512]; + int retval=0, len=0; + + ptr = (wl_network_t *)structure; + + /* packet type already determined */ + len += 2; + + /* Get net type */ + memset(temp, 0, sizeof(temp)); + retval = get_field(buffer + len, temp); + len += retval; + ptr->net_type = atoi(temp); + + /* Get channel */ + memset(temp, 0, sizeof(temp)); + retval = get_field(buffer + len, temp); + len += retval; + ptr->channel = atoi(temp); + + /* Set WEP y/n */ + memset(temp, 0, sizeof(temp)); + retval = get_field(buffer + len, temp); + len += retval; + ptr->wep = atoi(temp); + + /* Set MAC address */ + retval = get_field(buffer + len, ptr->mac); + len += retval; + + /* Set BSSID */ + retval = get_field(buffer + len, ptr->bssid); + + return 1; +} diff --git a/noncore/net/wellenreiter/libwellenreiter/source/proto.hh b/noncore/net/wellenreiter/libwellenreiter/source/wl_proto.hh index e3e9f50..f755589 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/proto.hh +++ b/noncore/net/wellenreiter/libwellenreiter/source/wl_proto.hh @@ -1,28 +1,28 @@ /* $Id$ */ -#ifndef PROTO_HH -#define PROTO_HH +#ifndef WLPROTO_HH +#define WLPROTO_HH #include <stdio.h> #include <string.h> #include <stdlib.h> /* Type definitions, to be continued */ #define NETFOUND 01 #define NETLOST 02 #define STARTSNIFF 98 #define STOPSNIFF 99 int send_network_found (const char *, int, void *); int get_network_found (void *, const char *); typedef struct { int net_type; /* 1 = Accesspoint ; 2 = Ad-Hoc */ int ssid_len; /* Length of SSID */ int channel; /* Channel */ int wep; /* 1 = WEP enabled ; 0 = disabled */ char mac[64]; /* MAC address of Accesspoint */ char bssid[128]; /* BSSID of Accesspoint */ } wl_network_t; -#endif /* PROTO_HH */ +#endif /* WLPROTO_HH */ diff --git a/noncore/net/wellenreiter/libwellenreiter/source/sock.cc b/noncore/net/wellenreiter/libwellenreiter/source/wl_sock.cc index 90fc20a..5714afb 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/sock.cc +++ b/noncore/net/wellenreiter/libwellenreiter/source/wl_sock.cc @@ -1,93 +1,91 @@ /* * Socket operations for wellenreiter * * $Id$ */ -#include "sock.hh" -#include "log.hh" +#include "wl_sock.hh" +#include "wl_log.hh" /* Setup UDP Socket for incoming commands */ -int commsock(const char *host, int port) +int wl_setupsock(const char *host, int port) { struct sockaddr_in saddr; int sock; if((sock=socket(AF_INET, SOCK_DGRAM, 0)) < 0) { wl_logerr("Cannot set up socket: %s", strerror(errno)); return -1; } memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = PF_INET; saddr.sin_port = htons(port); saddr.sin_addr.s_addr = inet_addr(host); if(bind(sock,(struct sockaddr *)&saddr, sizeof(saddr)) < 0) { wl_logerr("Cannot bind socket: %s", strerror(errno)); close(sock); return -1; } return sock; } /* Send a string to commsock */ -int sendcomm(const char *host, int port, const char *string, ...) +int wl_send(const char *host, int port, const char *string, ...) { int sock; char buffer[4096]; struct sockaddr_in saddr; va_list ap; /* Generate string */ memset(buffer, 0, sizeof(buffer)); va_start(ap, string); vsnprintf(buffer, sizeof(buffer)-1, string, ap); va_end(ap); saddr.sin_family = AF_INET; saddr.sin_port = htons(port); saddr.sin_addr.s_addr = inet_addr(host); /* Setup socket */ if((sock=socket(AF_INET, SOCK_DGRAM, 0)) < 0) { wl_logerr("Cannot set up socket: %s", strerror(errno)); return -1; } if(sendto(sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { wl_logerr("Cannot write to socket: %s", strerror(errno)); close(sock); - return 0; + return -1; } if(close(sock) < 0) wl_logerr("Cannot close socket: %s", strerror(errno)); - return 1; + return 0; } /* Check for new messages on commsock */ -int recvcomm(int *sock, char *out, int maxlen) +int wl_recv(int *sock, char *out, int maxlen) { struct sockaddr_in *cliaddr; socklen_t len=sizeof(struct sockaddr); - char buffer[128], retval[3]; - - memset(buffer, 0, sizeof(buffer)); - if(recvfrom(*sock, buffer, sizeof(buffer)-1, 0, (struct sockaddr *)cliaddr, &len) < 0) - return -1; + char retval[3]; memset(out, 0, maxlen); - memcpy(out, buffer, maxlen - 1); + if(recvfrom(*sock, out, maxlen - 1, 0, (struct sockaddr *)cliaddr, &len) < 0) + return -1; + /* Get packet type and return it */ memset(retval, 0, sizeof(retval)); memcpy(retval, out, 2); return atoi(retval); } diff --git a/noncore/net/wellenreiter/libwellenreiter/source/sock.hh b/noncore/net/wellenreiter/libwellenreiter/source/wl_sock.hh index 611e335..6ddbaef 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/sock.hh +++ b/noncore/net/wellenreiter/libwellenreiter/source/wl_sock.hh @@ -1,20 +1,20 @@ /* $id */ #ifndef WLSOCK_HH #define WLSOCK_HH #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> -int commsock(const char *, int); -int sendcomm(const char *, int, const char *, ...); -int recvcomm(int *, char *, int); +int wl_setupsock(const char *, int); +int wl_send(const char *, int, const char *, ...); +int wl_recv(int *, char *, int); #endif /* WLSOCK_HH */ |