$search
00001 /* 00002 * WPA Supplicant - driver interaction with old Broadcom wl.o driver 00003 * Copyright (c) 2004, Nikki Chumkov <nikki@gattaca.ru> 00004 * Copyright (c) 2004, Jouni Malinen <j@w1.fi> 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License version 2 as 00008 * published by the Free Software Foundation. 00009 * 00010 * Alternatively, this software may be distributed under the terms of BSD 00011 * license. 00012 * 00013 * See README and COPYING for more details. 00014 * 00015 * Please note that the newer Broadcom driver ("hybrid Linux driver") supports 00016 * Linux wireless extensions and does not need (or even work) with this old 00017 * driver wrapper. Use driver_wext.c with that driver. 00018 */ 00019 00020 #include "includes.h" 00021 00022 #include <sys/ioctl.h> 00023 00024 #include "common.h" 00025 00026 #if 0 00027 #include <netpacket/packet.h> 00028 #include <net/ethernet.h> /* the L2 protocols */ 00029 #else 00030 #include <linux/if_packet.h> 00031 #include <linux/if_ether.h> /* The L2 protocols */ 00032 #endif 00033 #include <net/if.h> 00034 #include <typedefs.h> 00035 00036 /* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys 00037 * WRT54G GPL tarball. */ 00038 #include <wlioctl.h> 00039 00040 #include "driver.h" 00041 #include "eloop.h" 00042 00043 struct wpa_driver_broadcom_data { 00044 void *ctx; 00045 int ioctl_sock; 00046 int event_sock; 00047 char ifname[IFNAMSIZ + 1]; 00048 }; 00049 00050 00051 #ifndef WLC_DEAUTHENTICATE 00052 #define WLC_DEAUTHENTICATE 143 00053 #endif 00054 #ifndef WLC_DEAUTHENTICATE_WITH_REASON 00055 #define WLC_DEAUTHENTICATE_WITH_REASON 201 00056 #endif 00057 #ifndef WLC_SET_TKIP_COUNTERMEASURES 00058 #define WLC_SET_TKIP_COUNTERMEASURES 202 00059 #endif 00060 00061 #if !defined(PSK_ENABLED) /* NEW driver interface */ 00062 #define WL_VERSION 360130 00063 /* wireless authentication bit vector */ 00064 #define WPA_ENABLED 1 00065 #define PSK_ENABLED 2 00066 00067 #define WAUTH_WPA_ENABLED(wauth) ((wauth) & WPA_ENABLED) 00068 #define WAUTH_PSK_ENABLED(wauth) ((wauth) & PSK_ENABLED) 00069 #define WAUTH_ENABLED(wauth) ((wauth) & (WPA_ENABLED | PSK_ENABLED)) 00070 00071 #define WSEC_PRIMARY_KEY WL_PRIMARY_KEY 00072 00073 typedef wl_wsec_key_t wsec_key_t; 00074 #endif 00075 00076 typedef struct { 00077 uint32 val; 00078 struct ether_addr ea; 00079 uint16 res; 00080 } wlc_deauth_t; 00081 00082 00083 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, 00084 void *timeout_ctx); 00085 00086 static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd, 00087 void *buf, int len) 00088 { 00089 struct ifreq ifr; 00090 wl_ioctl_t ioc; 00091 int ret = 0; 00092 00093 wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl(%s,%d,len=%d,val=%p)", 00094 drv->ifname, cmd, len, buf); 00095 /* wpa_hexdump(MSG_MSGDUMP, "BROADCOM: wlioctl buf", buf, len); */ 00096 00097 ioc.cmd = cmd; 00098 ioc.buf = buf; 00099 ioc.len = len; 00100 os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ); 00101 ifr.ifr_data = (caddr_t) &ioc; 00102 if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE, &ifr)) < 0) { 00103 if (cmd != WLC_GET_MAGIC) 00104 perror(ifr.ifr_name); 00105 wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl cmd=%d res=%d", 00106 cmd, ret); 00107 } 00108 00109 return ret; 00110 } 00111 00112 static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid) 00113 { 00114 struct wpa_driver_broadcom_data *drv = priv; 00115 if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0) 00116 return 0; 00117 00118 os_memset(bssid, 0, ETH_ALEN); 00119 return -1; 00120 } 00121 00122 static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid) 00123 { 00124 struct wpa_driver_broadcom_data *drv = priv; 00125 wlc_ssid_t s; 00126 00127 if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1) 00128 return -1; 00129 00130 os_memcpy(ssid, s.SSID, s.SSID_len); 00131 return s.SSID_len; 00132 } 00133 00134 static int wpa_driver_broadcom_set_wpa(void *priv, int enable) 00135 { 00136 struct wpa_driver_broadcom_data *drv = priv; 00137 unsigned int wauth, wsec; 00138 struct ether_addr ea; 00139 00140 os_memset(&ea, enable ? 0xff : 0, sizeof(ea)); 00141 if (broadcom_ioctl(drv, WLC_GET_WPA_AUTH, &wauth, sizeof(wauth)) == 00142 -1 || 00143 broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1) 00144 return -1; 00145 00146 if (enable) { 00147 wauth = PSK_ENABLED; 00148 wsec = TKIP_ENABLED; 00149 } else { 00150 wauth = 255; 00151 wsec &= ~(TKIP_ENABLED | AES_ENABLED); 00152 } 00153 00154 if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) == 00155 -1 || 00156 broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1) 00157 return -1; 00158 00159 /* FIX: magic number / error handling? */ 00160 broadcom_ioctl(drv, 122, &ea, sizeof(ea)); 00161 00162 return 0; 00163 } 00164 00165 static int wpa_driver_broadcom_set_key(const char *ifname, void *priv, 00166 enum wpa_alg alg, 00167 const u8 *addr, int key_idx, int set_tx, 00168 const u8 *seq, size_t seq_len, 00169 const u8 *key, size_t key_len) 00170 { 00171 struct wpa_driver_broadcom_data *drv = priv; 00172 int ret; 00173 wsec_key_t wkt; 00174 00175 os_memset(&wkt, 0, sizeof wkt); 00176 wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d", 00177 set_tx ? "PRIMARY " : "", key_idx, alg); 00178 if (key && key_len > 0) 00179 wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len); 00180 00181 switch (alg) { 00182 case WPA_ALG_NONE: 00183 wkt.algo = CRYPTO_ALGO_OFF; 00184 break; 00185 case WPA_ALG_WEP: 00186 wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */ 00187 break; 00188 case WPA_ALG_TKIP: 00189 wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */ 00190 break; 00191 case WPA_ALG_CCMP: 00192 wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM; 00193 * AES_OCB_MSDU, AES_OCB_MPDU? */ 00194 break; 00195 default: 00196 wkt.algo = CRYPTO_ALGO_NALG; 00197 break; 00198 } 00199 00200 if (seq && seq_len > 0) 00201 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len); 00202 00203 if (addr) 00204 wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN); 00205 00206 wkt.index = key_idx; 00207 wkt.len = key_len; 00208 if (key && key_len > 0) { 00209 os_memcpy(wkt.data, key, key_len); 00210 if (key_len == 32) { 00211 /* hack hack hack XXX */ 00212 os_memcpy(&wkt.data[16], &key[24], 8); 00213 os_memcpy(&wkt.data[24], &key[16], 8); 00214 } 00215 } 00216 /* wkt.algo = CRYPTO_ALGO_...; */ 00217 wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY; 00218 if (addr && set_tx) 00219 os_memcpy(&wkt.ea, addr, sizeof(wkt.ea)); 00220 ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt)); 00221 if (addr && set_tx) { 00222 /* FIX: magic number / error handling? */ 00223 broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea)); 00224 } 00225 return ret; 00226 } 00227 00228 00229 static void wpa_driver_broadcom_event_receive(int sock, void *ctx, 00230 void *sock_ctx) 00231 { 00232 char buf[8192]; 00233 int left; 00234 wl_wpa_header_t *wwh; 00235 union wpa_event_data data; 00236 u8 *resp_ies = NULL; 00237 00238 if ((left = recv(sock, buf, sizeof buf, 0)) < 0) 00239 return; 00240 00241 wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left); 00242 00243 if ((size_t) left < sizeof(wl_wpa_header_t)) 00244 return; 00245 00246 wwh = (wl_wpa_header_t *) buf; 00247 00248 if (wwh->snap.type != WL_WPA_ETHER_TYPE) 00249 return; 00250 if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0) 00251 return; 00252 00253 os_memset(&data, 0, sizeof(data)); 00254 00255 switch (wwh->type) { 00256 case WLC_ASSOC_MSG: 00257 left -= WL_WPA_HEADER_LEN; 00258 wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)", 00259 left); 00260 if (left > 0) { 00261 resp_ies = os_malloc(left); 00262 if (resp_ies == NULL) 00263 return; 00264 os_memcpy(resp_ies, buf + WL_WPA_HEADER_LEN, left); 00265 data.assoc_info.resp_ies = resp_ies; 00266 data.assoc_info.resp_ies_len = left; 00267 } 00268 00269 wpa_supplicant_event(ctx, EVENT_ASSOC, &data); 00270 os_free(resp_ies); 00271 break; 00272 case WLC_DISASSOC_MSG: 00273 wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE"); 00274 wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); 00275 break; 00276 case WLC_PTK_MIC_MSG: 00277 wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE"); 00278 data.michael_mic_failure.unicast = 1; 00279 wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); 00280 break; 00281 case WLC_GTK_MIC_MSG: 00282 wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE"); 00283 data.michael_mic_failure.unicast = 0; 00284 wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); 00285 break; 00286 default: 00287 wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)", 00288 wwh->type); 00289 break; 00290 } 00291 } 00292 00293 static void * wpa_driver_broadcom_init(void *ctx, const char *ifname) 00294 { 00295 int s; 00296 struct sockaddr_ll ll; 00297 struct wpa_driver_broadcom_data *drv; 00298 struct ifreq ifr; 00299 00300 /* open socket to kernel */ 00301 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 00302 perror("socket"); 00303 return NULL; 00304 } 00305 /* do it */ 00306 os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); 00307 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { 00308 perror(ifr.ifr_name); 00309 return NULL; 00310 } 00311 00312 00313 drv = os_zalloc(sizeof(*drv)); 00314 if (drv == NULL) 00315 return NULL; 00316 drv->ctx = ctx; 00317 os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); 00318 drv->ioctl_sock = s; 00319 00320 s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2)); 00321 if (s < 0) { 00322 perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))"); 00323 close(drv->ioctl_sock); 00324 os_free(drv); 00325 return NULL; 00326 } 00327 00328 os_memset(&ll, 0, sizeof(ll)); 00329 ll.sll_family = AF_PACKET; 00330 ll.sll_protocol = ntohs(ETH_P_802_2); 00331 ll.sll_ifindex = ifr.ifr_ifindex; 00332 ll.sll_hatype = 0; 00333 ll.sll_pkttype = PACKET_HOST; 00334 ll.sll_halen = 0; 00335 00336 if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) { 00337 perror("bind(netlink)"); 00338 close(s); 00339 close(drv->ioctl_sock); 00340 os_free(drv); 00341 return NULL; 00342 } 00343 00344 eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx, 00345 NULL); 00346 drv->event_sock = s; 00347 wpa_driver_broadcom_set_wpa(drv, 1); 00348 00349 return drv; 00350 } 00351 00352 static void wpa_driver_broadcom_deinit(void *priv) 00353 { 00354 struct wpa_driver_broadcom_data *drv = priv; 00355 wpa_driver_broadcom_set_wpa(drv, 0); 00356 eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); 00357 eloop_unregister_read_sock(drv->event_sock); 00358 close(drv->event_sock); 00359 close(drv->ioctl_sock); 00360 os_free(drv); 00361 } 00362 00363 static int wpa_driver_broadcom_set_countermeasures(void *priv, 00364 int enabled) 00365 { 00366 #if 0 00367 struct wpa_driver_broadcom_data *drv = priv; 00368 /* FIX: ? */ 00369 return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled, 00370 sizeof(enabled)); 00371 #else 00372 return 0; 00373 #endif 00374 } 00375 00376 static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled) 00377 { 00378 struct wpa_driver_broadcom_data *drv = priv; 00379 /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */ 00380 int _restrict = (enabled ? 1 : 0); 00381 00382 if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT, 00383 &_restrict, sizeof(_restrict)) < 0 || 00384 broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT, 00385 &_restrict, sizeof(_restrict)) < 0) 00386 return -1; 00387 00388 return 0; 00389 } 00390 00391 static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, 00392 void *timeout_ctx) 00393 { 00394 wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); 00395 wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); 00396 } 00397 00398 static int wpa_driver_broadcom_scan(void *priv, 00399 struct wpa_driver_scan_params *params) 00400 { 00401 struct wpa_driver_broadcom_data *drv = priv; 00402 wlc_ssid_t wst = { 0, "" }; 00403 const u8 *ssid = params->ssids[0].ssid; 00404 size_t ssid_len = params->ssids[0].ssid_len; 00405 00406 if (ssid && ssid_len > 0 && ssid_len <= sizeof(wst.SSID)) { 00407 wst.SSID_len = ssid_len; 00408 os_memcpy(wst.SSID, ssid, ssid_len); 00409 } 00410 00411 if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0) 00412 return -1; 00413 00414 eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); 00415 eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv, 00416 drv->ctx); 00417 return 0; 00418 } 00419 00420 00421 static const int frequency_list[] = { 00422 2412, 2417, 2422, 2427, 2432, 2437, 2442, 00423 2447, 2452, 2457, 2462, 2467, 2472, 2484 00424 }; 00425 00426 struct bss_ie_hdr { 00427 u8 elem_id; 00428 u8 len; 00429 u8 oui[3]; 00430 /* u8 oui_type; */ 00431 /* u16 version; */ 00432 } __attribute__ ((packed)); 00433 00434 static struct wpa_scan_results * 00435 wpa_driver_broadcom_get_scan_results(void *priv) 00436 { 00437 struct wpa_driver_broadcom_data *drv = priv; 00438 char *buf; 00439 wl_scan_results_t *wsr; 00440 wl_bss_info_t *wbi; 00441 size_t ap_num; 00442 struct wpa_scan_results *res; 00443 00444 buf = os_malloc(WLC_IOCTL_MAXLEN); 00445 if (buf == NULL) 00446 return NULL; 00447 00448 wsr = (wl_scan_results_t *) buf; 00449 00450 wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr); 00451 wsr->version = 107; 00452 wsr->count = 0; 00453 00454 if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) { 00455 os_free(buf); 00456 return NULL; 00457 } 00458 00459 res = os_zalloc(sizeof(*res)); 00460 if (res == NULL) { 00461 os_free(buf); 00462 return NULL; 00463 } 00464 00465 res->res = os_zalloc(wsr->count * sizeof(struct wpa_scan_res *)); 00466 if (res->res == NULL) { 00467 os_free(res); 00468 os_free(buf); 00469 return NULL; 00470 } 00471 00472 for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) { 00473 struct wpa_scan_res *r; 00474 r = os_malloc(sizeof(*r) + wbi->ie_length); 00475 if (r == NULL) 00476 break; 00477 res->res[res->num++] = r; 00478 00479 os_memcpy(r->bssid, &wbi->BSSID, ETH_ALEN); 00480 r->freq = frequency_list[wbi->channel - 1]; 00481 /* get ie's */ 00482 os_memcpy(r + 1, wbi + 1, wbi->ie_length); 00483 r->ie_len = wbi->ie_length; 00484 00485 wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length); 00486 } 00487 00488 wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu " 00489 "BSSes)", 00490 wsr->buflen, (unsigned long) ap_num); 00491 00492 os_free(buf); 00493 return res; 00494 } 00495 00496 static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr, 00497 int reason_code) 00498 { 00499 struct wpa_driver_broadcom_data *drv = priv; 00500 wlc_deauth_t wdt; 00501 wdt.val = reason_code; 00502 os_memcpy(&wdt.ea, addr, sizeof wdt.ea); 00503 wdt.res = 0x7fff; 00504 return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt, 00505 sizeof(wdt)); 00506 } 00507 00508 static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr, 00509 int reason_code) 00510 { 00511 struct wpa_driver_broadcom_data *drv = priv; 00512 return broadcom_ioctl(drv, WLC_DISASSOC, NULL, 0); 00513 } 00514 00515 static int 00516 wpa_driver_broadcom_associate(void *priv, 00517 struct wpa_driver_associate_params *params) 00518 { 00519 struct wpa_driver_broadcom_data *drv = priv; 00520 wlc_ssid_t s; 00521 int infra = 1; 00522 int auth = 0; 00523 int wsec = 4; 00524 int dummy; 00525 int wpa_auth; 00526 int ret; 00527 00528 ret = wpa_driver_broadcom_set_drop_unencrypted( 00529 drv, params->drop_unencrypted); 00530 00531 s.SSID_len = params->ssid_len; 00532 os_memcpy(s.SSID, params->ssid, params->ssid_len); 00533 00534 switch (params->pairwise_suite) { 00535 case CIPHER_WEP40: 00536 case CIPHER_WEP104: 00537 wsec = 1; 00538 break; 00539 00540 case CIPHER_TKIP: 00541 wsec = 2; 00542 break; 00543 00544 case CIPHER_CCMP: 00545 wsec = 4; 00546 break; 00547 00548 default: 00549 wsec = 0; 00550 break; 00551 } 00552 00553 switch (params->key_mgmt_suite) { 00554 case KEY_MGMT_802_1X: 00555 wpa_auth = 1; 00556 break; 00557 00558 case KEY_MGMT_PSK: 00559 wpa_auth = 2; 00560 break; 00561 00562 default: 00563 wpa_auth = 255; 00564 break; 00565 } 00566 00567 /* printf("broadcom_associate: %u %u %u\n", pairwise_suite, 00568 * group_suite, key_mgmt_suite); 00569 * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec)); 00570 * wl join uses wlc_sec_wep here, not wlc_set_wsec */ 00571 00572 if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 || 00573 broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth, 00574 sizeof(wpa_auth)) < 0 || 00575 broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 || 00576 broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 || 00577 broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 || 00578 broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 || 00579 broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0) 00580 return -1; 00581 00582 return ret; 00583 } 00584 00585 const struct wpa_driver_ops wpa_driver_broadcom_ops = { 00586 .name = "broadcom", 00587 .desc = "Broadcom wl.o driver", 00588 .get_bssid = wpa_driver_broadcom_get_bssid, 00589 .get_ssid = wpa_driver_broadcom_get_ssid, 00590 .set_key = wpa_driver_broadcom_set_key, 00591 .init = wpa_driver_broadcom_init, 00592 .deinit = wpa_driver_broadcom_deinit, 00593 .set_countermeasures = wpa_driver_broadcom_set_countermeasures, 00594 .scan2 = wpa_driver_broadcom_scan, 00595 .get_scan_results2 = wpa_driver_broadcom_get_scan_results, 00596 .deauthenticate = wpa_driver_broadcom_deauthenticate, 00597 .disassociate = wpa_driver_broadcom_disassociate, 00598 .associate = wpa_driver_broadcom_associate, 00599 };