author | max <max> | 2003-03-04 14:04:52 (UTC) |
---|---|---|
committer | max <max> | 2003-03-04 14:04:52 (UTC) |
commit | 3adca473d5440b00e15781627e00465350e9118b (patch) (unidiff) | |
tree | e60e57f10c8297eee39f9e31974f9199013c4930 | |
parent | 7cf53b4030437bdbd405b8b16684ba9f68e2891d (diff) | |
download | opie-3adca473d5440b00e15781627e00465350e9118b.zip opie-3adca473d5440b00e15781627e00465350e9118b.tar.gz opie-3adca473d5440b00e15781627e00465350e9118b.tar.bz2 |
start_sniff
6 files changed, 135 insertions, 34 deletions
diff --git a/noncore/net/wellenreiter/daemon/source/daemon.cc b/noncore/net/wellenreiter/daemon/source/daemon.cc index 3a28217..b840f17 100644 --- a/noncore/net/wellenreiter/daemon/source/daemon.cc +++ b/noncore/net/wellenreiter/daemon/source/daemon.cc | |||
@@ -6,24 +6,25 @@ | |||
6 | 6 | ||
7 | #include "config.hh" | 7 | #include "config.hh" |
8 | #include "daemon.hh" | 8 | #include "daemon.hh" |
9 | 9 | ||
10 | /* should be parsed from cfg-file */ | 10 | /* should be parsed from cfg-file */ |
11 | #define MAXCHANNEL 13 | 11 | #define MAXCHANNEL 13 |
12 | #define CHANINTERVAL 500000 | 12 | #define CHANINTERVAL 500000 |
13 | 13 | ||
14 | |||
14 | /* Main function of wellenreiterd */ | 15 | /* Main function of wellenreiterd */ |
15 | int main(int argc, char **argv) | 16 | int main(int argc, char **argv) |
16 | { | 17 | { |
17 | int sock, maxfd, retval; | 18 | int sock, maxfd, retval; |
18 | char buffer[WL_SOCKBUF]; | 19 | char buffer[WL_SOCKBUF]; |
19 | struct pcap_pkthdr header; | 20 | struct pcap_pkthdr header; |
20 | struct sockaddr_in saddr; | 21 | struct sockaddr_in saddr; |
21 | pcap_t *handletopcap; | 22 | // pcap_t *handletopcap; |
22 | wl_cardtype_t cardtype; | 23 | wl_cardtype_t cardtype; |
23 | pthread_t sub; | 24 | pthread_t sub; |
24 | const unsigned char *packet; | 25 | const unsigned char *packet; |
25 | 26 | ||
26 | fd_set rset; | 27 | fd_set rset; |
27 | 28 | ||
28 | fprintf(stderr, "wellenreiterd %s\n\n", VERSION); | 29 | fprintf(stderr, "wellenreiterd %s\n\n", VERSION); |
29 | fprintf(stderr, "(c) 2002 by M-M-M\n\n"); | 30 | fprintf(stderr, "(c) 2002 by M-M-M\n\n"); |
@@ -35,36 +36,24 @@ int main(int argc, char **argv) | |||
35 | memset(cardtype.iface, 0, sizeof(cardtype.iface)); | 36 | memset(cardtype.iface, 0, sizeof(cardtype.iface)); |
36 | strncpy(cardtype.iface, (char *)argv[1], sizeof(cardtype.iface) - 1); | 37 | strncpy(cardtype.iface, (char *)argv[1], sizeof(cardtype.iface) - 1); |
37 | 38 | ||
38 | /* Set card type */ | 39 | /* Set card type */ |
39 | cardtype.type = atoi(argv[2]); | 40 | cardtype.type = atoi(argv[2]); |
40 | if(cardtype.type < 1 || cardtype.type > 4) | 41 | if(cardtype.type < 1 || cardtype.type > 4) |
41 | usage(); | 42 | usage(); |
42 | 43 | ||
43 | /* set card into monitor mode */ | 44 | /* Until we do not act as a read daemon, it starts the sniffer |
44 | if(!card_into_monitormode(&handletopcap, cardtype.iface, | 45 | right after startup */ |
45 | cardtype.type)) | 46 | if (!start_sniffer(cardtype.iface,cardtype.type)) |
46 | { | 47 | { |
47 | wl_logerr("Cannot initialize the wireless-card, aborting"); | 48 | wl_logerr("daemon, start_sniff did not return proper, aborting"); |
48 | exit(EXIT_FAILURE); | 49 | exit(EXIT_FAILURE); |
49 | } | 50 | } |
50 | wl_loginfo("Set card into monitor mode"); | 51 | wl_loginfo ("daemon, wireless card prepared for sniffing"); |
51 | |||
52 | /* setup pcap */ | ||
53 | if((handletopcap = pcap_open_live(cardtype.iface, | ||
54 | BUFSIZ, 1, 0, NULL)) == NULL) | ||
55 | { | ||
56 | wl_logerr("pcap_open_live() failed: %s", strerror(errno)); | ||
57 | exit(EXIT_FAILURE); | ||
58 | } | ||
59 | |||
60 | #ifdef HAVE_PCAP_NONBLOCK | ||
61 | pcap_setnonblock(handletopcap, 1, NULL); | ||
62 | #endif | ||
63 | 52 | ||
64 | /* Setup socket for incoming commands */ | 53 | /* Setup socket for incoming commands */ |
65 | if((sock=wl_setupsock(DAEMONADDR, DAEMONPORT, saddr)) < 0) | 54 | if((sock=wl_setupsock(DAEMONADDR, DAEMONPORT, saddr)) < 0) |
66 | { | 55 | { |
67 | wl_logerr("Cannot setup socket"); | 56 | wl_logerr("Cannot setup socket"); |
68 | exit(EXIT_FAILURE); | 57 | exit(EXIT_FAILURE); |
69 | } | 58 | } |
70 | wl_loginfo("Set up socket '%d' for GUI communication", sock); | 59 | wl_loginfo("Set up socket '%d' for GUI communication", sock); |
diff --git a/noncore/net/wellenreiter/libwellenreiter/source/cardmode.cc b/noncore/net/wellenreiter/libwellenreiter/source/cardmode.cc index 7c9fbc4..4f187c0 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/cardmode.cc +++ b/noncore/net/wellenreiter/libwellenreiter/source/cardmode.cc | |||
@@ -1,19 +1,19 @@ | |||
1 | /* | 1 | /* |
2 | * Set card modes for sniffing | 2 | * Set card modes for sniffing |
3 | * | 3 | * |
4 | * $Id$ | 4 | * $Id$ |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include "cardmode.hh" | 7 | #include "cardmode.hh" |
8 | #include "wl_log.hh" | 8 | #include "wl_log.hh" |
9 | 9 | pcap_t *handletopcap; | |
10 | /* main card into monitor function */ | 10 | /* main card into monitor function */ |
11 | int card_into_monitormode (pcap_t **orighandle, const char *device, int cardtype) | 11 | int card_into_monitormode (const char *device, int cardtype) |
12 | { | 12 | { |
13 | char CiscoRFMON[35] = "/proc/driver/aironet/"; | 13 | char CiscoRFMON[35] = "/proc/driver/aironet/"; |
14 | FILE *CISCO_CONFIG_FILE; | 14 | FILE *CISCO_CONFIG_FILE; |
15 | 15 | ||
16 | /* Checks if we have a device to sniff on */ | 16 | /* Checks if we have a device to sniff on */ |
17 | if(device == NULL) | 17 | if(device == NULL) |
18 | { | 18 | { |
19 | wl_logerr("No device given"); | 19 | wl_logerr("No device given"); |
@@ -147,16 +147,61 @@ int card_check_rfmon_datalink (const char *device) | |||
147 | } | 147 | } |
148 | else | 148 | else |
149 | { | 149 | { |
150 | wl_loginfo("Your successfully listen on %s in 802.11 raw mode", device); | 150 | wl_loginfo("Your successfully listen on %s in 802.11 raw mode", device); |
151 | return 1; | 151 | return 1; |
152 | } | 152 | } |
153 | } | 153 | } |
154 | 154 | ||
155 | /* Ipaq running familiar does not have a loopback device, we need one */ | ||
156 | int check_loopback() | ||
157 | { | ||
158 | /* Checking for a loopback interface with 127.0.0.1, otherwise the other stuff seems to fail on | ||
159 | familiar linux on ipaq's */ | ||
160 | int err; | ||
161 | /* First generate a socket to use with iocalls */ | ||
162 | int fd = socket(AF_INET, SOCK_DGRAM, 0); | ||
163 | if (fd < 0) | ||
164 | { | ||
165 | /* In case of an error */ | ||
166 | wl_logerr("check_loopback, generation of a socket failed, cannot continue"); | ||
167 | return 0; | ||
168 | } | ||
169 | /* Fill an empty an interface structure with the right flags (UP and Promsic) */ | ||
170 | struct ifreq ifr; | ||
171 | strncpy(ifr.ifr_name, "lo",3); | ||
172 | |||
173 | /* Get the interface flags, loopback interfaces can be detected that way */ | ||
174 | err = ioctl(fd, SIOCGIFFLAGS, &ifr); | ||
175 | if (err < 0) | ||
176 | { | ||
177 | wl_logerr("check_loopback, could not get the flags of lo, check if you got a lo loopback interface, cannot continue"); | ||
178 | close(fd); | ||
179 | return 0; | ||
180 | } | ||
181 | /* Checking the flags for IFF_LOOPBACK flags */ | ||
182 | if(ifr.ifr_flags && IFF_LOOPBACK) | ||
183 | { | ||
184 | /* Yes, we do have a loopback interface....sup! */ | ||
185 | close(fd); | ||
186 | wl_loginfo ("check_loopback, check for loopback interface lo successful"); | ||
187 | return 1; | ||
188 | } | ||
189 | else | ||
190 | { | ||
191 | wl_logerr("check_loopback, did not found an interface lo with the IFF_LOOPBACK flag set, cannot continue"); | ||
192 | close(fd); | ||
193 | return 0; | ||
194 | } | ||
195 | /* Should never be reached */ | ||
196 | return 0; | ||
197 | } /*check_loopback */ | ||
198 | |||
199 | |||
155 | /* Set card into promisc mode */ | 200 | /* Set card into promisc mode */ |
156 | int card_set_promisc_up (const char *device) | 201 | int card_set_promisc_up (const char *device) |
157 | { | 202 | { |
158 | int err; | 203 | int err; |
159 | /* First generate a socket to use with iocalls */ | 204 | /* First generate a socket to use with iocalls */ |
160 | int fd = socket(AF_INET, SOCK_DGRAM, 0); | 205 | int fd = socket(AF_INET, SOCK_DGRAM, 0); |
161 | if (fd < 0) | 206 | if (fd < 0) |
162 | { | 207 | { |
@@ -210,26 +255,16 @@ int card_remove_promisc (const char *device) | |||
210 | { | 255 | { |
211 | /* In case of an error */ | 256 | /* In case of an error */ |
212 | perror("socket"); | 257 | perror("socket"); |
213 | return 0; | 258 | return 0; |
214 | } | 259 | } |
215 | 260 | ||
216 | /* Fill an empty an interface structure with the right flags (UP and Promsic) */ | 261 | /* Fill an empty an interface structure with the right flags (UP and Promsic) */ |
217 | struct ifreq ifr; | 262 | struct ifreq ifr; |
218 | /* strncpy(ifr.ifr_name, device,10); | ||
219 | ifr.ifr_flags = IFF_UP + IFF_PROMISC; | ||
220 | err = ioctl(fd, SIOCSIFFLAGS, &ifr); | ||
221 | if (err < 0) | ||
222 | { | ||
223 | perror("Could not access the interface, "); | ||
224 | close(fd); | ||
225 | return 0; | ||
226 | } | ||
227 | */ | ||
228 | /* Get the flags from the interface*/ | 263 | /* Get the flags from the interface*/ |
229 | strncpy(ifr.ifr_name, device,10); | 264 | strncpy(ifr.ifr_name, device,10); |
230 | err = ioctl(fd, SIOCGIFFLAGS, &ifr); | 265 | err = ioctl(fd, SIOCGIFFLAGS, &ifr); |
231 | if (err < 0) | 266 | if (err < 0) |
232 | { | 267 | { |
233 | perror("Could not access the interface, "); | 268 | perror("Could not access the interface, "); |
234 | close(fd); | 269 | close(fd); |
235 | return 0; | 270 | return 0; |
diff --git a/noncore/net/wellenreiter/libwellenreiter/source/cardmode.hh b/noncore/net/wellenreiter/libwellenreiter/source/cardmode.hh index 58e99ac..73e0ae1 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/cardmode.hh +++ b/noncore/net/wellenreiter/libwellenreiter/source/cardmode.hh | |||
@@ -33,16 +33,18 @@ typedef unsigned long long u64; | |||
33 | #endif | 33 | #endif |
34 | 34 | ||
35 | extern "C" | 35 | extern "C" |
36 | { | 36 | { |
37 | #include <net/bpf.h> | 37 | #include <net/bpf.h> |
38 | #include <pcap.h> | 38 | #include <pcap.h> |
39 | } | 39 | } |
40 | 40 | ||
41 | extern pcap_t *handletopcap; | ||
42 | |||
41 | /* Defines, used for the card setup */ | 43 | /* Defines, used for the card setup */ |
42 | #define DEFAULT_PATH "/proc/driver/aironet/%s/Config" | 44 | #define DEFAULT_PATH "/proc/driver/aironet/%s/Config" |
43 | #define CISCO_STATUS "/proc/driver/aironet/%s/Status" | 45 | #define CISCO_STATUS "/proc/driver/aironet/%s/Status" |
44 | #define CARD_TYPE_CISCO 1 | 46 | #define CARD_TYPE_CISCO 1 |
45 | #define CARD_TYPE_NG 2 | 47 | #define CARD_TYPE_NG 2 |
46 | #define CARD_TYPE_HOSTAP 3 | 48 | #define CARD_TYPE_HOSTAP 3 |
47 | #define CARD_TYPE_ORINOCCO 4 | 49 | #define CARD_TYPE_ORINOCCO 4 |
48 | 50 | ||
@@ -53,26 +55,26 @@ extern "C" | |||
53 | 55 | ||
54 | 56 | ||
55 | /* only for now, until we have the daemon running */ | 57 | /* only for now, until we have the daemon running */ |
56 | /*the config file should provide these information */ | 58 | /*the config file should provide these information */ |
57 | #define CARD_TYPE CARD_TYPE_HOSTAP | 59 | #define CARD_TYPE CARD_TYPE_HOSTAP |
58 | 60 | ||
59 | /* Prototypes */ | 61 | /* Prototypes */ |
60 | int card_check_rfmon_datalink (const char *device); | 62 | int card_check_rfmon_datalink (const char *device); |
61 | int card_into_monitormode (pcap_t **, const char *, int); | 63 | int card_into_monitormode (const char *, int); |
64 | int check_loopback(); | ||
62 | int card_set_promisc_up (const char *device); | 65 | int card_set_promisc_up (const char *device); |
63 | int card_remove_promisc (const char *device); | 66 | int card_remove_promisc (const char *device); |
64 | int card_set_channel (const char *device, int channel,int cardtype); | 67 | int card_set_channel (const char *device, int channel,int cardtype); |
65 | int iw_get_range_info(int skfd, const char * ifname, struct iw_range * range); | 68 | int iw_get_range_info(int skfd, const char * ifname, struct iw_range * range); |
66 | double iw_freq2float(iw_freq * in); | 69 | double iw_freq2float(iw_freq * in); |
67 | void iw_float2freq(double in, iw_freq *out); | 70 | void iw_float2freq(double in, iw_freq *out); |
68 | int card_detect_channels (char * device); | 71 | int card_detect_channels (char * device); |
69 | 72 | ||
70 | |||
71 | /*------------------------------------------------------------------*/ | 73 | /*------------------------------------------------------------------*/ |
72 | /* | 74 | /* |
73 | * Wrapper to push some Wireless Parameter in the driver | 75 | * Wrapper to push some Wireless Parameter in the driver |
74 | */ | 76 | */ |
75 | static inline int | 77 | static inline int |
76 | iw_set_ext(int skfd, /* Socket to the kernel */ | 78 | iw_set_ext(int skfd, /* Socket to the kernel */ |
77 | char * ifname, /* Device name */ | 79 | char * ifname, /* Device name */ |
78 | int request,/* WE ID */ | 80 | int request,/* WE ID */ |
diff --git a/noncore/net/wellenreiter/libwellenreiter/source/sniff.cc b/noncore/net/wellenreiter/libwellenreiter/source/sniff.cc index 6e512c4..0616a7e 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/sniff.cc +++ b/noncore/net/wellenreiter/libwellenreiter/source/sniff.cc | |||
@@ -5,16 +5,87 @@ | |||
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include "sniff.hh" | 7 | #include "sniff.hh" |
8 | #include "ieee802_11.hh" | 8 | #include "ieee802_11.hh" |
9 | #include "extract.hh" | 9 | #include "extract.hh" |
10 | #include "wl_log.hh" | 10 | #include "wl_log.hh" |
11 | #include "wl_types.hh" | 11 | #include "wl_types.hh" |
12 | #include "wl_proto.hh" | 12 | #include "wl_proto.hh" |
13 | #include "cardmode.hh" | ||
14 | |||
15 | int start_sniffer(const char *device, int cardtype ) | ||
16 | { | ||
17 | |||
18 | /* This function initialize the sniffing | ||
19 | 1. Check for lo interface | ||
20 | 2. bring it into promsicous mode and UP | ||
21 | 3. bring device into rfmon mode | ||
22 | start the pcap sniffing process. | ||
23 | */ | ||
24 | |||
25 | /* Do we have the device name ? */ | ||
26 | if(device == NULL) | ||
27 | { | ||
28 | wl_logerr("start_sniffer, parameter \"device\" is empty, please check your config"); | ||
29 | return 0; | ||
30 | } | ||
31 | |||
32 | /* Some Linux System does not have a loopback device lo with 127.0.0.1 so sockets could | ||
33 | not made correctly, let the proggie check that and proceed only if it exists. */ | ||
34 | if (!check_loopback()) | ||
35 | { | ||
36 | wl_logerr("start_sniffer, check_loopback failed, cannot continue without a loopback"); | ||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | /* Set the card into regulary promiscous mode first and set the UP flag, in case no ip | ||
41 | was given. It would work without the promisc flags but i dont like this */ | ||
42 | if (!card_set_promisc_up(device)) | ||
43 | { | ||
44 | wl_logerr("start_sniffer, card_set_promisc_up failed, cannot continue"); | ||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | /* Set card into the rfmon/monitoring mode */ | ||
49 | if (!card_into_monitormode(device,cardtype)) | ||
50 | { | ||
51 | wl_logerr("start_sniffer, cannot put wireless card into monitoring mode, aborting"); | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | /* setup pcap handle, used for the packet decoding etc. */ | ||
56 | if((handletopcap = pcap_open_live((char *) device, BUFSIZ, 1, 0, NULL)) == NULL) | ||
57 | { | ||
58 | wl_logerr("pcap_open_live() failed: %s", strerror(errno)); | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | #ifdef HAVE_PCAP_NONBLOCK | ||
63 | pcap_setnonblock(handletopcap, 1, NULL); | ||
64 | #endif | ||
65 | return 1; | ||
66 | } | ||
67 | |||
68 | |||
69 | int stop_sniffer(const char *device, int cardtype) | ||
70 | { | ||
71 | /* This function terminates the sniffing | ||
72 | 1. get the device state | ||
73 | 2. remove the rfmon state | ||
74 | 3. Remove the promisc state | ||
75 | start the pcap sniffing process. | ||
76 | |||
77 | */ | ||
78 | |||
79 | /* Do we really have at least a lo interface with the 127.0.0.1 ? */ | ||
80 | return 0; | ||
81 | |||
82 | } | ||
83 | |||
13 | 84 | ||
14 | /* Main function, checks packets */ | 85 | /* Main function, checks packets */ |
15 | void process_packets(const struct pcap_pkthdr *pkthdr, | 86 | void process_packets(const struct pcap_pkthdr *pkthdr, |
16 | const unsigned char *packet, | 87 | const unsigned char *packet, |
17 | char *guihost, | 88 | char *guihost, |
18 | int guiport) | 89 | int guiport) |
19 | { | 90 | { |
20 | unsigned int caplen = pkthdr->caplen; | 91 | unsigned int caplen = pkthdr->caplen; |
diff --git a/noncore/net/wellenreiter/libwellenreiter/source/sniff.hh b/noncore/net/wellenreiter/libwellenreiter/source/sniff.hh index c7108ac..a4cf4b7 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/sniff.hh +++ b/noncore/net/wellenreiter/libwellenreiter/source/sniff.hh | |||
@@ -35,21 +35,24 @@ struct packetinfo | |||
35 | int channel; | 35 | int channel; |
36 | char bssid[sizeof("00:00:00:00:00:00") + 1]; | 36 | char bssid[sizeof("00:00:00:00:00:00") + 1]; |
37 | char desthwaddr[sizeof("00:00:00:00:00:00") + 1]; | 37 | char desthwaddr[sizeof("00:00:00:00:00:00") + 1]; |
38 | char sndhwaddr[sizeof("00:00:00:00:00:00") + 1]; | 38 | char sndhwaddr[sizeof("00:00:00:00:00:00") + 1]; |
39 | char ssid[128]; | 39 | char ssid[128]; |
40 | int ssid_len; | 40 | int ssid_len; |
41 | }; | 41 | }; |
42 | 42 | ||
43 | /* Function definitions */ | ||
44 | /* Used for stoping and starting the sniffer process */ | ||
45 | int start_sniffer(const char *device, int cardtype); | ||
46 | int stop_sniffer(const char *device, int cardtype); | ||
43 | void process_packets(const struct pcap_pkthdr* pkthdr,const u_char* packet, char *, int); | 47 | void process_packets(const struct pcap_pkthdr* pkthdr,const u_char* packet, char *, int); |
44 | int decode_80211b_hdr(const u_char *p,struct packetinfo *ppinfo); | 48 | int decode_80211b_hdr(const u_char *p,struct packetinfo *ppinfo); |
45 | void etheraddr_string(register const u_char *ep,char * text); | 49 | void etheraddr_string(register const u_char *ep,char * text); |
46 | int handle_beacon(u_int16_t fc, const u_char *p,struct packetinfo *ppinfo); | 50 | int handle_beacon(u_int16_t fc, const u_char *p,struct packetinfo *ppinfo); |
47 | |||
48 | int GetHeaderLength(u_int16_t fc); | 51 | int GetHeaderLength(u_int16_t fc); |
49 | 52 | ||
50 | /* | 53 | /* |
51 | * True if "l" bytes of "var" were captured. | 54 | * True if "l" bytes of "var" were captured. |
52 | * | 55 | * |
53 | * The "snapend - (l) <= snapend" checks to make sure "l" isn't so large | 56 | * The "snapend - (l) <= snapend" checks to make sure "l" isn't so large |
54 | * that "snapend - (l)" underflows. | 57 | * that "snapend - (l)" underflows. |
55 | * | 58 | * |
diff --git a/noncore/net/wellenreiter/libwellenreiter/source/wl_types.hh b/noncore/net/wellenreiter/libwellenreiter/source/wl_types.hh index cd482fe..afc105c 100644 --- a/noncore/net/wellenreiter/libwellenreiter/source/wl_types.hh +++ b/noncore/net/wellenreiter/libwellenreiter/source/wl_types.hh | |||
@@ -33,10 +33,11 @@ typedef struct { | |||
33 | char mac[64]; /* MAC address of Accesspoint */ | 33 | char mac[64]; /* MAC address of Accesspoint */ |
34 | char bssid[128]; /* BSSID of Net */ | 34 | char bssid[128]; /* BSSID of Net */ |
35 | } wl_network_t; | 35 | } wl_network_t; |
36 | 36 | ||
37 | /* Config specific */ | 37 | /* Config specific */ |
38 | #define WL_CONFFILE "sample.conf" | 38 | #define WL_CONFFILE "sample.conf" |
39 | #define WL_CONFBUFF 128 | 39 | #define WL_CONFBUFF 128 |
40 | 40 | ||
41 | |||
41 | #endif /* WL_TYPES_HH */ | 42 | #endif /* WL_TYPES_HH */ |
42 | 43 | ||