/*++ Copyright(c) 1998,99 Microsoft Corporation Module Name: params.c Abstract: Windows Load Balancing Service (WLBS) Driver - registry parameter support Author: kyrilf --*/ #include #include #include "wlbsparm.h" #include "params.h" #include "univ.h" #include "log.h" #include "main.h" /* To create registry path strings for the new port rule structure in the registry. */ extern int swprintf(wchar_t * buffer, const wchar_t * format,...); /* CONSTANTS */ #define PARAMS_INFO_BUF_SIZE ((CVY_MAX_PARAM_STR + 1) * sizeof (WCHAR) + \ CVY_MAX_RULES * sizeof (CVY_RULE) + \ sizeof (KEY_VALUE_PARTIAL_INFORMATION) + 4) /* The maximum length of a registry path string, in characters (WCHAR). */ #define CVY_MAX_REG_PATH 512 /* GLOBALS */ static ULONG log_module_id = LOG_MODULE_PARAMS; static UCHAR infop [PARAMS_INFO_BUF_SIZE]; /* PROCEDURES */ /* return codes from Params_verify call */ /* in user mode returns object ID of the dialog control that needs to be fixed */ #define CVY_VERIFY_OK 0 #define CVY_VERIFY_EXIT 1 ULONG Params_verify ( PVOID nlbctxt, PCVY_PARAMS paramp, BOOL complain) { ULONG j, i, code; PCVY_RULE rp, rulep; PSTR prot; PCHAR aff; ULONG ret; ANSI_STRING domain, key, ip; PMAIN_CTXT ctxtp = (PMAIN_CTXT)nlbctxt; /* ensure sane values for all parameters */ CVY_CHECK_MIN (paramp -> alive_period, CVY_MIN_ALIVE_PERIOD); CVY_CHECK_MAX (paramp -> alive_period, CVY_MAX_ALIVE_PERIOD); CVY_CHECK_MIN (paramp -> alive_tolerance, CVY_MIN_ALIVE_TOLER); CVY_CHECK_MAX (paramp -> alive_tolerance, CVY_MAX_ALIVE_TOLER); CVY_CHECK_MIN (paramp -> num_actions, CVY_MIN_NUM_ACTIONS); CVY_CHECK_MAX (paramp -> num_actions, CVY_MAX_NUM_ACTIONS); CVY_CHECK_MIN (paramp -> num_packets, CVY_MIN_NUM_PACKETS); CVY_CHECK_MAX (paramp -> num_packets, CVY_MAX_NUM_PACKETS); CVY_CHECK_MIN (paramp -> dscr_per_alloc, CVY_MIN_DSCR_PER_ALLOC); CVY_CHECK_MAX (paramp -> dscr_per_alloc, CVY_MAX_DSCR_PER_ALLOC); CVY_CHECK_MIN (paramp -> max_dscr_allocs, CVY_MIN_MAX_DSCR_ALLOCS); CVY_CHECK_MAX (paramp -> max_dscr_allocs, CVY_MAX_MAX_DSCR_ALLOCS); CVY_CHECK_MAX (paramp -> cleanup_delay, CVY_MAX_CLEANUP_DELAY); CVY_CHECK_MAX (paramp -> ip_chg_delay, CVY_MAX_IP_CHG_DELAY); CVY_CHECK_MIN (paramp -> num_send_msgs, (CVY_MAX_HOSTS + 1) * 2); CVY_CHECK_MAX (paramp -> num_send_msgs, (CVY_MAX_HOSTS + 1) * 10); /* verify that parameters are correct */ if (paramp -> parms_ver != CVY_PARAMS_VERSION) { UNIV_PRINT (("bad parameters version %d, expected %d", paramp -> parms_ver, CVY_PARAMS_VERSION)); LOG_MSG (MSG_WARN_VERSION, MSG_NONE); return CVY_VERIFY_EXIT; } if (paramp -> rct_port < CVY_MIN_RCT_PORT || paramp -> rct_port > CVY_MAX_RCT_PORT) { UNIV_PRINT (("bad value for parameter %s, %d <= %d <= %d", CVY_NAME_NUM_RULES, CVY_MIN_CLEANUP_DELAY, paramp -> cleanup_delay, CVY_MAX_CLEANUP_DELAY)); return CVY_VERIFY_EXIT; } if (paramp -> host_priority < CVY_MIN_HOST_PRIORITY || paramp -> host_priority > CVY_MAX_HOSTS) { UNIV_PRINT (("bad value for parameter %s, %d <= %d <= %d", CVY_NAME_HOST_PRIORITY, CVY_MIN_HOST_PRIORITY, paramp -> host_priority, CVY_MAX_HOSTS)); return CVY_VERIFY_EXIT; } if (paramp -> num_rules > CVY_MAX_NUM_RULES) { UNIV_PRINT (("bad value for parameter %s, %d <= %d <= %d", CVY_NAME_NUM_RULES, CVY_MIN_NUM_RULES, paramp -> num_rules, CVY_MAX_NUM_RULES)); return CVY_VERIFY_EXIT; } if (paramp -> num_rules > CVY_MAX_USABLE_RULES) { UNIV_PRINT (("bad value for parameter %s, %d <= %d <= %d", CVY_NAME_NUM_RULES, CVY_MIN_NUM_RULES, paramp -> num_rules, CVY_MAX_USABLE_RULES)); LOG_MSG2 (MSG_WARN_TOO_MANY_RULES, MSG_NONE, paramp -> num_rules, CVY_MAX_USABLE_RULES); paramp -> num_rules = CVY_MAX_USABLE_RULES; } CVY_CHECK_MAX (paramp -> num_rules, CVY_MAX_NUM_RULES); CVY_CHECK_MIN (paramp -> host_priority, CVY_MIN_HOST_PRIORITY); CVY_CHECK_MAX (paramp -> host_priority, CVY_MAX_HOST_PRIORITY); #ifdef TRACE_PARAMS DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_HOST_PRIORITY, paramp -> host_priority); DbgPrint ("Parameter: %-25ls = %ls\n", CVY_NAME_DED_IP_ADDR, paramp -> ded_ip_addr); DbgPrint ("Parameter: %-25ls = %ls\n", CVY_NAME_DED_NET_MASK, paramp -> ded_net_mask); DbgPrint ("Parameter: %-25ls = %ls\n", CVY_NAME_NETWORK_ADDR, paramp -> cl_mac_addr); DbgPrint ("Parameter: %-25ls = %ls\n", CVY_NAME_CL_IP_ADDR, paramp -> cl_ip_addr); DbgPrint ("Parameter: %-25ls = %ls\n", CVY_NAME_CL_NET_MASK, paramp -> cl_net_mask); DbgPrint ("Parameter: %-25ls = %ls\n", CVY_NAME_DOMAIN_NAME, paramp -> domain_name); DbgPrint ("Parameter: %-25ls = %ls\n", CVY_NAME_MCAST_IP_ADDR, paramp -> cl_igmp_addr); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_ALIVE_PERIOD, paramp -> alive_period); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_ALIVE_TOLER, paramp -> alive_tolerance); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_NUM_ACTIONS, paramp -> num_actions); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_NUM_PACKETS, paramp -> num_packets); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_NUM_SEND_MSGS, paramp -> num_send_msgs); DbgPrint ("Parameter: %-25ls = %x\n", CVY_NAME_INSTALL_DATE, paramp -> install_date); DbgPrint ("Parameter: %-25ls = %x\n", CVY_NAME_RMT_PASSWORD, paramp -> rmt_password); DbgPrint ("Parameter: %-25ls = %x\n", CVY_NAME_RCT_PASSWORD, paramp -> rct_password); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_RCT_PORT, paramp -> rct_port); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_RCT_ENABLED, paramp -> rct_enabled); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_NUM_RULES, paramp -> num_rules); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_DSCR_PER_ALLOC,paramp -> dscr_per_alloc); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_MAX_DSCR_ALLOCS, paramp -> max_dscr_allocs); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_SCALE_CLIENT, paramp -> scale_client); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_CLEANUP_DELAY, paramp -> cleanup_delay); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_NBT_SUPPORT, paramp -> nbt_support); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_MCAST_SUPPORT, paramp -> mcast_support); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_MCAST_SPOOF, paramp -> mcast_spoof); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_IGMP_SUPPORT, paramp -> igmp_support); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_MASK_SRC_MAC, paramp -> mask_src_mac); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_NETMON_ALIVE, paramp -> netmon_alive); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_IP_CHG_DELAY, paramp -> ip_chg_delay); DbgPrint ("Parameter: %-25ls = %d\n", CVY_NAME_CONVERT_MAC, paramp -> convert_mac); DbgPrint ("\n"); DbgPrint ("Bi-directional affinity teaming:\n"); DbgPrint ("Active: %ls\n", (paramp->bda_teaming.active) ? L"Yes" : L"No"); DbgPrint ("Team ID: %ls\n", (paramp->bda_teaming.team_id)); DbgPrint ("Master: %ls\n", (paramp->bda_teaming.master) ? L"Yes" : L"No"); DbgPrint ("Reverse hash: %ls\n", (paramp->bda_teaming.reverse_hash) ? L"Yes" : L"No"); DbgPrint ("\n"); /* If teaming is active, check some required configuration. */ if (paramp->bda_teaming.active) { /* If there is no team ID, bail out. User-level stuff should ensure that it is a GUID. We'll just check for an empty string here. */ if (paramp->bda_teaming.team_id[0] == L'\0') { DbgPrint("BDA Teaming: Invalid Team ID (Empty)\n"); LOG_MSG (MSG_ERROR_BDA_PARAMS_TEAM_ID, MSG_NONE); paramp->bda_teaming.active = FALSE; return CVY_VERIFY_EXIT; } /* If there is more than one port rule, bail out. None is fine. */ if (paramp->num_rules > 1) { DbgPrint("BDA Teaming: Invalid number of port rules specified (%d)\n", paramp->num_rules); LOG_MSG (MSG_ERROR_BDA_PARAMS_PORT_RULES, MSG_NONE); paramp->bda_teaming.active = FALSE; return CVY_VERIFY_EXIT; } /* Since we are asserting that there are only 0 or 1 rules, we know that the only rule we need to check (if there even is a rule), is at the beginning of the array, so we can simply set a pointer to the beginning of the port rules array in the params structure. */ rp = paramp->port_rules; /* If there is one rule and its multiple host filtering, the affinity must be single or class C. If its not (that is, if its set to no affinity), bail out. */ if ((paramp->num_rules == 1) && (rp->mode == CVY_MULTI) && (rp->mode_data.multi.affinity == CVY_AFFINITY_NONE)) { DbgPrint("BDA Teaming: Invalid affinity for multiple host filtering (None)\n"); LOG_MSG (MSG_ERROR_BDA_PARAMS_PORT_RULES, MSG_NONE); paramp->bda_teaming.active = FALSE; return CVY_VERIFY_EXIT; } } DbgPrint ("Rules:\n"); for (i = 0; i < paramp -> num_rules * sizeof (CVY_RULE) / sizeof (ULONG); i ++) { if (i != 0 && i % 9 == 0) DbgPrint ("\n"); DbgPrint ("%08X ", * ((PULONG) paramp -> port_rules + i)); } DbgPrint ("\n VIP Start End Prot Mode Pri Load Affinity\n"); #endif for (i = 0; i < paramp -> num_rules; i ++) { rp = paramp -> port_rules + i; code = CVY_DRIVER_RULE_CODE_GET (rp); CVY_DRIVER_RULE_CODE_SET (rp); if (code != CVY_DRIVER_RULE_CODE_GET (rp)) { UNIV_PRINT (("bad rule code: %x vs %x", code, rp -> code)); return CVY_VERIFY_EXIT; } if (rp -> start_port > CVY_MAX_PORT) { UNIV_PRINT (("bad value for rule parameter %s, %d <= %d <= %d", "StartPort", CVY_MIN_PORT, rp -> start_port, CVY_MAX_PORT)); return CVY_VERIFY_EXIT; } if (rp -> end_port > CVY_MAX_PORT) { UNIV_PRINT (("bad value for rule parameter %s, %d <= %d <= %d", "EndPort", CVY_MIN_PORT, rp -> end_port, CVY_MAX_PORT)); return CVY_VERIFY_EXIT; } if (rp -> protocol < CVY_MIN_PROTOCOL || rp -> protocol > CVY_MAX_PROTOCOL) { UNIV_PRINT (("bad value for rule parameter %s, %d <= %d <= %d", "Protocol", CVY_MIN_PROTOCOL, rp -> protocol, CVY_MAX_PROTOCOL)); return CVY_VERIFY_EXIT; } if (rp -> mode < CVY_MIN_MODE || rp -> mode > CVY_MAX_MODE) { UNIV_PRINT (("bad value for parameter %s, %d <= %d <= %d", "Mode", CVY_MIN_MODE, rp -> mode, CVY_MAX_MODE)); return CVY_VERIFY_EXIT; } #ifdef TRACE_PARAMS switch (rp -> protocol) { case CVY_TCP: prot = "TCP"; break; case CVY_UDP: prot = "UDP"; break; default: prot = "Both"; break; } DbgPrint ("%08x %5d %5d %4s ", rp -> virtual_ip_addr, rp -> start_port, rp -> end_port, prot); #endif switch (rp -> mode) { case CVY_SINGLE: if (rp -> mode_data . single . priority < CVY_MIN_PRIORITY || rp -> mode_data . single . priority > CVY_MAX_PRIORITY) { UNIV_PRINT (("bad value for parameter %s, %d <= %d <= %d", "Priority", CVY_MIN_PRIORITY, rp -> mode_data . single . priority, CVY_MAX_PRIORITY)); return CVY_VERIFY_EXIT; } #ifdef TRACE_PARAMS DbgPrint ("%8s %3d\n", "Single", rp -> mode_data . single . priority); #endif break; case CVY_MULTI: if (rp -> mode_data . multi . affinity < CVY_MIN_AFFINITY || rp -> mode_data . multi . affinity > CVY_MAX_AFFINITY) { UNIV_PRINT (("bad value for parameter %s, %d <= %d <= %d", "Affinity", CVY_MIN_AFFINITY, rp -> mode_data.multi.affinity, CVY_MAX_AFFINITY)); return CVY_VERIFY_EXIT; } if (rp -> mode_data . multi . affinity == CVY_AFFINITY_NONE) aff = "None"; else if (rp -> mode_data . multi . affinity == CVY_AFFINITY_SINGLE) aff = "Single"; else aff = "Class C"; if (rp -> mode_data . multi . equal_load) { #ifdef TRACE_PARAMS DbgPrint ("%8s %3s %4s %s\n", "Multiple", "", "Eql", aff); #endif } else { if (rp -> mode_data . multi . load > CVY_MAX_LOAD) { UNIV_PRINT (("bad value for parameter %s, %d <= %d <= %d", "Load", CVY_MIN_LOAD, rp -> mode_data . multi . load, CVY_MAX_LOAD)); return CVY_VERIFY_EXIT; } #ifdef TRACE_PARAMS DbgPrint ("%8s %3s %4d %s\n", "Multiple", "", rp -> mode_data . multi . load, aff); #endif } break; default: #ifdef TRACE_PARAMS DbgPrint ("%8s\n", "Disabled"); #endif break; } if (rp -> start_port > rp -> end_port) { UNIV_PRINT (("Bad port range %d - %d", rp -> start_port, rp -> end_port)); return CVY_VERIFY_EXIT; } for (j = 0; j < i; j ++) { rulep = paramp -> port_rules + j; if ((rulep -> virtual_ip_addr == rp -> virtual_ip_addr) && ((rulep -> start_port < rp -> start_port && rulep -> end_port >= rp -> start_port) || (rulep -> start_port >= rp -> start_port && rulep -> start_port <= rp -> end_port))) { UNIV_PRINT (("Requested port range in rule %d VIP: %08x (%d - %d) overlaps with the range in an existing rule %d VIP: %08x (%d - %d)", i, rp -> virtual_ip_addr, rp -> start_port, rp -> end_port, j, rulep -> virtual_ip_addr, rulep -> start_port, rulep -> end_port)); return CVY_VERIFY_EXIT; } } } rulep = &(paramp->port_rules[0]); /* in optimized mode - if we have no rules, or a single rule that will not look at anything or only source IP address (the only exception to this is multiple handling mode with no affinity that also uses source port for its decision making) then we can just rely on normal mechanism to handle every fragmented packet - since algorithm will not attempt to look past the IP header. for multiple rules, or single rule with no affinity, apply algorithm only to the first packet that has UDP/TCP header and then let fragmented packets up on all of the systems. TCP will then do the right thing and throw away the fragments on all of the systems other than the one that handled the first fragment. */ /* ###### ramkrish. Setting the optimized frags flag in ctxtp is probably a hack, but let it be until a better solution can be thought of. */ if (paramp->num_rules == 0 || (paramp->num_rules == 1 && rulep->start_port == CVY_MIN_PORT && rulep->end_port == CVY_MAX_PORT && ! (rulep->mode == CVY_MULTI && rulep->mode_data.multi.affinity == CVY_AFFINITY_NONE))) { ctxtp -> optimized_frags = TRUE; #ifdef TRACE_PARAMS DbgPrint("IP fragmentation mode - OPTIMIZED\n"); #endif } else { ctxtp -> optimized_frags = FALSE; #ifdef TRACE_PARAMS DbgPrint("IP fragmentation mode - UNOPTIMIZED\n"); #endif } return CVY_VERIFY_OK; } /* end Params_verify */ static NTSTATUS Params_query_registry ( PVOID nlbctxt, PCVY_PARAMS paramp, HANDLE hdl, PWCHAR name, PVOID datap, ULONG len) { UNICODE_STRING str; ULONG actual; NTSTATUS status; PUCHAR buffer; PKEY_VALUE_PARTIAL_INFORMATION valp = (PKEY_VALUE_PARTIAL_INFORMATION) infop; PMAIN_CTXT ctxtp = (PMAIN_CTXT)nlbctxt; RtlInitUnicodeString (& str, name); status = ZwQueryValueKey (hdl, & str, KeyValuePartialInformation, infop, PARAMS_INFO_BUF_SIZE, & actual); if (status != STATUS_SUCCESS) { UNIV_PRINT (("error %x querying value %ls", status, name)); return status; } if (valp -> Type == REG_DWORD) { if (valp -> DataLength != sizeof (ULONG)) { UNIV_PRINT (("bad DWORD length %d", valp -> DataLength)); return STATUS_OBJECT_NAME_NOT_FOUND; } * ((PULONG) datap) = * ((PULONG) valp -> Data); } else if (valp -> Type == REG_BINARY) { /* since we know that only port rules are of binary type - check the size here */ if (valp -> DataLength > len) { UNIV_PRINT (("bad BINARY length %d", valp -> DataLength)); LOG_MSG3 (MSG_ERROR_INTERNAL, MSG_NONE, valp -> DataLength, sizeof (CVY_RULE), CVY_MAX_RULES - 1); return STATUS_OBJECT_NAME_NOT_FOUND; } RtlCopyMemory (datap, valp -> Data, valp -> DataLength); } else { if (valp -> DataLength == 0) { /* simulate an empty string */ valp -> DataLength = 2; valp -> Data [0] = 0; valp -> Data [1] = 0; } if (valp -> DataLength > len) { UNIV_PRINT (("string too big for %ls %d %d\n", name, valp -> DataLength, len)); } RtlCopyMemory (datap, valp -> Data, valp -> DataLength <= len ? valp -> DataLength : len); } return status; } /* end Params_query_registry */ LONG Params_read_portrules (PVOID nlbctxt, PWCHAR reg_path, PCVY_PARAMS paramp) { ULONG dwTemp; ULONG pr_index; ULONG pr_reg_path_length; WCHAR pr_reg_path[CVY_MAX_REG_PATH]; WCHAR pr_number[8]; UNICODE_STRING pr_path; OBJECT_ATTRIBUTES pr_obj; HANDLE pr_hdl = NULL; NTSTATUS status = STATUS_SUCCESS; NTSTATUS final_status = STATUS_SUCCESS; PMAIN_CTXT ctxtp = (PMAIN_CTXT)nlbctxt; /* Make sure that we have AT LEAST 12 WCHARs left for the port rule path information (\ + PortRules + \ + NUL). */ ASSERT(wcslen(reg_path) < (CVY_MAX_REG_PATH - wcslen(CVY_NAME_PORT_RULES) - 3)); /* Create the "static" portion of the registry, which is "...\Services\WLBS\Interface\\PortRules\". */ wcscpy(pr_reg_path, (PWSTR)reg_path); wcscat(pr_reg_path, L"\\"); wcscat(pr_reg_path, CVY_NAME_PORT_RULES); wcscat(pr_reg_path, L"\\"); /* Get the length of this string - this is the placeholder where we will sprintf in the rule numbers each time. */ pr_reg_path_length = wcslen(pr_reg_path); /* Make sure that we have AT LEAST 3 WCHARs left for the port rule number and the NUL character (XX + NUL). */ ASSERT(pr_reg_path_length < (CVY_MAX_REG_PATH - 3)); /* Loop through each port rule in the port rules tree. */ for (pr_index = 0; pr_index < paramp->num_rules; pr_index++) { /* Sprintf the port rule number (+1) into the registry path key after the "PortRules\". */ swprintf(pr_number, L"%d", pr_index + 1); wcscpy(pr_reg_path + pr_reg_path_length, (PWSTR)pr_number); UNIV_PRINT(("Port rule registry path: %ls", pr_reg_path)); RtlInitUnicodeString(&pr_path, pr_reg_path); InitializeObjectAttributes(&pr_obj, &pr_path, 0, NULL, NULL); /* Open the key - upon failure, bail out below. */ status = ZwOpenKey(&pr_hdl, KEY_READ, &pr_obj); /* If we can't open this key, note FAILURE and continue to the next rule. */ if (status != STATUS_SUCCESS) { final_status = status; continue; } /* Read the rule (error checking) code for the port rule. */ status = Params_query_registry(nlbctxt, paramp, pr_hdl, CVY_NAME_CODE, ¶mp->port_rules[pr_index].code, sizeof(paramp->port_rules[pr_index].code)); if (status != STATUS_SUCCESS) final_status = status; { WCHAR szTemp[CVY_MAX_VIRTUAL_IP_ADDR + 1]; PWCHAR pTemp = szTemp; ULONG dwOctets[4]; ULONG cIndex; /* Read the virtual IP address to which this rule applies. */ status = Params_query_registry(nlbctxt, paramp, pr_hdl, CVY_NAME_VIP, szTemp, sizeof(szTemp)); if (status != STATUS_SUCCESS) { final_status = status; } else { for (cIndex = 0; cIndex < 4; cIndex++, pTemp++) { if (!Univ_str_to_ulong(dwOctets + cIndex, pTemp, &pTemp, 3, 10) || (cIndex < 3 && *pTemp != L'.')) { UNIV_PRINT (("bad virtual IP address")); final_status = STATUS_INVALID_PARAMETER; break; } } } IP_SET_ADDR(¶mp->port_rules[pr_index].virtual_ip_addr, dwOctets[0], dwOctets[1], dwOctets[2], dwOctets[3]); } /* Read the start port. */ status = Params_query_registry(nlbctxt, paramp, pr_hdl, CVY_NAME_START_PORT, ¶mp->port_rules[pr_index].start_port, sizeof(paramp->port_rules[pr_index].start_port)); if (status != STATUS_SUCCESS) final_status = status; /* Read the end port. */ status = Params_query_registry(nlbctxt, paramp, pr_hdl, CVY_NAME_END_PORT, ¶mp->port_rules[pr_index].end_port, sizeof(paramp->port_rules[pr_index].end_port)); if (status != STATUS_SUCCESS) final_status = status; /* Read the protocol(s) to which this rule applies. */ status = Params_query_registry(nlbctxt, paramp, pr_hdl, CVY_NAME_PROTOCOL, ¶mp->port_rules[pr_index].protocol, sizeof(paramp->port_rules[pr_index].protocol)); if (status != STATUS_SUCCESS) final_status = status; /* Read the filtering mode - single host, multiple host or disabled. */ status = Params_query_registry(nlbctxt, paramp, pr_hdl, CVY_NAME_MODE, ¶mp->port_rules[pr_index].mode, sizeof(paramp->port_rules[pr_index].mode)); if (status != STATUS_SUCCESS) final_status = status; /* Based on the mode, get the rest of the parameters. */ switch (paramp->port_rules[pr_index].mode) { case CVY_SINGLE: /* Read the single host filtering priority. */ status = Params_query_registry(nlbctxt, paramp, pr_hdl, CVY_NAME_PRIORITY, ¶mp->port_rules[pr_index].mode_data.single.priority, sizeof(paramp->port_rules[pr_index].mode_data.single.priority)); if (status != STATUS_SUCCESS) final_status = status; break; case CVY_MULTI: /* Read the equal load flag for the multiple host filtering rule. Because this parameter is a DWORD in the registry and a USHORT in our parameters structure, we read it into a temporary variable and then copy to our parameters upon success. */ status = Params_query_registry(nlbctxt, paramp, pr_hdl, CVY_NAME_EQUAL_LOAD, &dwTemp, sizeof(dwTemp)); if (status != STATUS_SUCCESS) final_status = status; else paramp->port_rules[pr_index].mode_data.multi.equal_load = (USHORT)dwTemp; /* Read the explicit load distribution for multiple host filtering. */ status = Params_query_registry(nlbctxt, paramp, pr_hdl, CVY_NAME_LOAD, ¶mp->port_rules[pr_index].mode_data.multi.load, sizeof(paramp->port_rules[pr_index].mode_data.multi.load)); if (status != STATUS_SUCCESS) final_status = status; /* Read the affinity setting for multiple host filtering. Because this parameter is a DWORD in the registry and a USHORT in our parameters structure, we read it into a temporary variable and then copy to our parameters upon success. */ status = Params_query_registry(nlbctxt, paramp, pr_hdl, CVY_NAME_AFFINITY, &dwTemp, sizeof(dwTemp)); if (status != STATUS_SUCCESS) final_status = status; else paramp->port_rules[pr_index].mode_data.multi.affinity = (USHORT)dwTemp; break; } /* Close the key for this rule. */ status = ZwClose (pr_hdl); if (status != STATUS_SUCCESS) final_status = status; } return final_status; } LONG Params_read_teaming (PVOID nlbctxt, PWCHAR reg_path, PCVY_PARAMS paramp) { WCHAR bda_reg_path[CVY_MAX_REG_PATH]; UNICODE_STRING bda_path; OBJECT_ATTRIBUTES bda_obj; HANDLE bda_hdl = NULL; NTSTATUS status = STATUS_SUCCESS; NTSTATUS final_status = STATUS_SUCCESS; PMAIN_CTXT ctxtp = (PMAIN_CTXT)nlbctxt; /* Make sure that we have AT LEAST 12 WCHARs left for the BDA teaming path information (\ + BDATeaming + NUL). */ ASSERT(wcslen(reg_path) < (CVY_MAX_REG_PATH - wcslen(CVY_NAME_BDA_TEAMING) - 2)); /* Create the registry path, which is "...\Services\WLBS\Interface\\BDATeaming\". */ wcscpy(bda_reg_path, (PWSTR)reg_path); wcscat(bda_reg_path, L"\\"); wcscat(bda_reg_path, CVY_NAME_BDA_TEAMING); UNIV_PRINT(("BDA teaming registry path: %ls", bda_reg_path)); RtlInitUnicodeString(&bda_path, bda_reg_path); InitializeObjectAttributes(&bda_obj, &bda_path, 0, NULL, NULL); /* Open the key - failure is acceptable and means that this adapter is not part of a BDA team. */ status = ZwOpenKey(&bda_hdl, KEY_READ, &bda_obj); /* If we can't open this key, return here. */ if (status != STATUS_SUCCESS) { /* If we couldn't find the key, that's fine - it might not exist. */ if (status == STATUS_OBJECT_NAME_NOT_FOUND) return STATUS_SUCCESS; /* Otherwise, there was a legitimate error. */ else return status; } /* If we were able to open the registry key, then this adapter is part of a BDA team. */ paramp->bda_teaming.active = TRUE; /* Read the team ID from the registry - this is a GUID. */ status = Params_query_registry (nlbctxt, paramp, bda_hdl, CVY_NAME_BDA_TEAM_ID, ¶mp->bda_teaming.team_id, sizeof(paramp->bda_teaming.team_id)); if (status != STATUS_SUCCESS) final_status = status; /* Get the boolean indication of whether or not this adapter is the master for the team. */ status = Params_query_registry (nlbctxt, paramp, bda_hdl, CVY_NAME_BDA_MASTER, ¶mp->bda_teaming.master, sizeof(paramp->bda_teaming.master)); if (status != STATUS_SUCCESS) final_status = status; /* Get the boolean indication of forward (conventional) or reverse hashing. */ status = Params_query_registry (nlbctxt, paramp, bda_hdl, CVY_NAME_BDA_REVERSE_HASH, ¶mp->bda_teaming.reverse_hash, sizeof(paramp->bda_teaming.reverse_hash)); if (status != STATUS_SUCCESS) final_status = status; /* Close the key for BDA teaming. */ status = ZwClose(bda_hdl); if (status != STATUS_SUCCESS) final_status = status; return final_status; } LONG Params_read_hostname (PVOID nlbctxt, PCVY_PARAMS paramp) { WCHAR domain[CVY_MAX_HOST_NAME + 1]; WCHAR hostname[CVY_MAX_HOST_NAME + 1]; WCHAR host_reg_path[CVY_MAX_REG_PATH]; UNICODE_STRING host_path; OBJECT_ATTRIBUTES host_obj; HANDLE host_hdl = NULL; NTSTATUS status = STATUS_SUCCESS; PMAIN_CTXT ctxtp = (PMAIN_CTXT)nlbctxt; /* Erase the hostname.domain first. */ wcscpy(paramp->hostname, L""); /* Create the registry path to the TCP/IP parameters. */ wcscpy(host_reg_path, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"); UNIV_PRINT(("TCP/IP parameters registry path: %ls", host_reg_path)); RtlInitUnicodeString(&host_path, host_reg_path); InitializeObjectAttributes(&host_obj, &host_path, 0, NULL, NULL); /* Open the key - failure is acceptable. */ status = ZwOpenKey(&host_hdl, KEY_READ, &host_obj); /* If we can't open this key, return here. */ if (status != STATUS_SUCCESS) return STATUS_SUCCESS; /* Get the hostname from the registry. Failure is acceptable. */ status = Params_query_registry (nlbctxt, paramp, host_hdl, L"Hostname", hostname, sizeof(hostname)); if (status != STATUS_SUCCESS) return STATUS_SUCCESS; /* Read the domain from the registry. Failture is acceptable. */ status = Params_query_registry (nlbctxt, paramp, host_hdl, L"Domain", domain, sizeof(domain)); if (status != STATUS_SUCCESS) return STATUS_SUCCESS; /* If the domain.hostname is too large to fit in the buffer, then we return here. */ if ((wcslen(domain) + wcslen(hostname) + 1) > CVY_MAX_HOST_NAME) { /* However, if we at least have room for the hostname, use it. */ if (wcslen(hostname) <= CVY_MAX_HOST_NAME) wcscpy(paramp->hostname, hostname); return STATUS_SUCCESS; } /* If we succesfully retrieved the domain and hostname, and we have enough room in our buffer, create the fully qualified hostname as HOST.DOMAIN. */ wcscpy(paramp->hostname, hostname); wcscat(paramp->hostname, L"."); wcscat(paramp->hostname, domain); return STATUS_SUCCESS; } LONG Params_init ( PVOID nlbctxt, PVOID rp, PVOID adapter_guid, PCVY_PARAMS paramp) { NTSTATUS status; WCHAR reg_path [CVY_MAX_REG_PATH]; UNICODE_STRING path; OBJECT_ATTRIBUTES obj; HANDLE hdl = NULL; NTSTATUS final_status = STATUS_SUCCESS; PMAIN_CTXT ctxtp = (PMAIN_CTXT)nlbctxt; RtlZeroMemory (paramp, sizeof (CVY_PARAMS)); paramp -> cl_mac_addr [0] = 0; paramp -> cl_ip_addr [0] = 0; paramp -> cl_net_mask [0] = 0; paramp -> ded_ip_addr [0] = 0; paramp -> ded_net_mask [0] = 0; paramp -> cl_igmp_addr [0] = 0; paramp -> domain_name [0] = 0; /* Setup defaults that we HAVE to have. */ paramp->num_actions = CVY_DEF_NUM_ACTIONS; paramp->num_packets = CVY_DEF_NUM_PACKETS; paramp->num_send_msgs = CVY_DEF_NUM_SEND_MSGS; paramp->alive_period = CVY_DEF_ALIVE_PERIOD; /* Make sure that the registry path fits in out buffer. */ ASSERT(wcslen((PWSTR) rp) + wcslen((PWSTR) adapter_guid) + 1 <= CVY_MAX_REG_PATH); wcscpy (reg_path, (PWSTR) rp); wcscat (reg_path, (PWSTR) adapter_guid); RtlInitUnicodeString (& path, reg_path); InitializeObjectAttributes (& obj, & path, 0, NULL, NULL); status = ZwOpenKey (& hdl, KEY_READ, & obj); if (status != STATUS_SUCCESS) goto error; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_VERSION, & paramp -> parms_ver, sizeof (paramp -> parms_ver)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_EFFECTIVE_VERSION, & paramp -> effective_ver, sizeof (paramp -> effective_ver)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_HOST_PRIORITY, & paramp -> host_priority, sizeof (paramp -> host_priority)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_NETWORK_ADDR, & paramp -> cl_mac_addr, sizeof (paramp -> cl_mac_addr)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_CL_IP_ADDR, & paramp -> cl_ip_addr, sizeof (paramp -> cl_ip_addr)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_MCAST_IP_ADDR, & paramp -> cl_igmp_addr, sizeof (paramp -> cl_igmp_addr)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_CL_NET_MASK, & paramp -> cl_net_mask, sizeof (paramp -> cl_net_mask)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_ALIVE_PERIOD, & paramp -> alive_period, sizeof (paramp -> alive_period)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_ALIVE_TOLER, & paramp -> alive_tolerance, sizeof (paramp -> alive_tolerance)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_DOMAIN_NAME, & paramp -> domain_name, sizeof (paramp -> domain_name)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_RMT_PASSWORD, & paramp -> rmt_password, sizeof (paramp -> rmt_password)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_RCT_PASSWORD, & paramp -> rct_password, sizeof (paramp -> rct_password)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_RCT_PORT, & paramp -> rct_port, sizeof (paramp -> rct_port)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_RCT_ENABLED, & paramp -> rct_enabled, sizeof (paramp -> rct_enabled)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_NUM_ACTIONS, & paramp -> num_actions, sizeof (paramp -> num_actions)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_NUM_PACKETS, & paramp -> num_packets, sizeof (paramp -> num_packets)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_NUM_SEND_MSGS, & paramp -> num_send_msgs, sizeof (paramp -> num_send_msgs)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_INSTALL_DATE, & paramp -> install_date, sizeof (paramp -> install_date)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_CLUSTER_MODE, & paramp -> cluster_mode, sizeof (paramp -> cluster_mode)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_DED_IP_ADDR, & paramp -> ded_ip_addr, sizeof (paramp -> ded_ip_addr)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_DED_NET_MASK, & paramp -> ded_net_mask, sizeof (paramp -> ded_net_mask)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_DSCR_PER_ALLOC, & paramp -> dscr_per_alloc, sizeof (paramp -> dscr_per_alloc)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_MAX_DSCR_ALLOCS, & paramp -> max_dscr_allocs, sizeof (paramp -> max_dscr_allocs)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_SCALE_CLIENT, & paramp -> scale_client, sizeof (paramp -> scale_client)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_CLEANUP_DELAY, & paramp -> cleanup_delay, sizeof (paramp -> cleanup_delay)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_NBT_SUPPORT, & paramp -> nbt_support, sizeof (paramp -> nbt_support)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_MCAST_SUPPORT, & paramp -> mcast_support, sizeof (paramp -> mcast_support)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_MCAST_SPOOF, & paramp -> mcast_spoof, sizeof (paramp -> mcast_spoof)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_IGMP_SUPPORT, & paramp -> igmp_support, sizeof (paramp -> igmp_support)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_MASK_SRC_MAC, & paramp -> mask_src_mac, sizeof (paramp -> mask_src_mac)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_NETMON_ALIVE, & paramp -> netmon_alive, sizeof (paramp -> netmon_alive)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_IP_CHG_DELAY, & paramp -> ip_chg_delay, sizeof (paramp -> ip_chg_delay)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_CONVERT_MAC, & paramp -> convert_mac, sizeof (paramp -> convert_mac)); if (status != STATUS_SUCCESS) final_status = status; status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_NUM_RULES, & paramp -> num_rules, sizeof (paramp -> num_rules)); /* Get the port rules only if we were able to successfully grab the number of port rules, which is necessary in order to generate the registry keys for the new port rule format for virtual cluster support. */ if (status != STATUS_SUCCESS) { final_status = status; } else { /* First try to open the port rules in their old format. */ status = Params_query_registry (nlbctxt, paramp, hdl, CVY_NAME_OLD_PORT_RULES, & paramp -> port_rules, sizeof (paramp -> port_rules)); if (status == STATUS_SUCCESS) { /* If we were succussful in reading the rules from the old settings, then FAIL - this shouldn't happen. */ final_status = STATUS_INVALID_PARAMETER; UNIV_PRINT(("Error: Found port rules in old binary format")); } else { /* Look up the port rules, which we are now expecting to be in tact in the new location. */ status = Params_read_portrules(nlbctxt, reg_path, paramp); if (status != STATUS_SUCCESS) final_status = status; } } /* Look up the BDA teaming parameters, if they exist. */ status = Params_read_teaming(nlbctxt, reg_path, paramp); if (status != STATUS_SUCCESS) final_status = status; /* Attempt to retrieve the hostname from the TCP/IP registry settings. */ status = Params_read_hostname(nlbctxt, paramp); if (status != STATUS_SUCCESS) final_status = status; status = ZwClose (hdl); if (status != STATUS_SUCCESS) final_status = status; status = final_status; error: if (status != STATUS_SUCCESS) { UNIV_PRINT (("error querying registry %x", status)); LOG_MSG1 (MSG_ERROR_REGISTRY, path . Buffer, status); } /* Verify registry parameter settings. */ if (Params_verify (nlbctxt, paramp, TRUE) != CVY_VERIFY_OK) { UNIV_PRINT (("bad parameter value(s)")); LOG_MSG (MSG_ERROR_REGISTRY, MSG_NONE); return STATUS_UNSUCCESSFUL; } return status; } /* end Params_init */