MSCS

Windows Server 2003 中的 Microsoft 群集服务简介

Windows Server 2003 系列中的 Microsoft 群集服务 (MSCS) 简介

发布日期 : 4/1/2004 | 更新日期 : 4/1/2004

Mohan Rao Cavale

Microsoft Corporation

2002 年 11 月

适用于:

Windows Server 2003

Windows Server 2003 企业版

Windows Server 2003 数据中心版

Microsoft 群集服务

摘要:了解如何在群集环境中对应用程序轻松执行性能检查,而无需更改应用程序代码。本文着重介绍三种支持群集的 Microsoft 服务器技术之一:群集服务。 (15 页打印页)

内容

本页内容

简介 简介
三种群集技术 三种群集技术
通过 Microsoft 群集服务实现故障转移 通过 Microsoft 群集服务实现故障转移
群集服务结构 群集服务结构
不识别群集的应用程序 不识别群集的应用程序
高可用性记事本 高可用性记事本
结论 结论

简介

在任何情况下,仅仅交付一个具有丰富功能集的高质量应用程序是不够的,在越来越多的时候,它还必须满足高可用性标准。您是否因为群集技术看起来过于高深、难于理解和使用而没有使应用程序再提高一个层次? 随着 Microsoft 的群集服务在 Windows NT™ 4 中引入以及在 Windows Server 2003 系列中正式提供,开发人员可使用一些简单工具在群集环境中部署应用程序。这些工具能够将群集中的应用程序登记为一般应用程序,并且能够通过编写 Windows 脚本的方式来控制应用程序的配置。

群集将两个或多个服务器连接在一起,使其对客户端呈现为单个计算机。在一个群集中连接服务器可以分担工作负载、实现单点操作/管理,并为满足增长的需求进行相应的调整提供了一种途径。因此,通过群集可以产生具有高可用性的应用程序。

本文着重介绍三种支持群集的 Microsoft 服务器技术之一:群集服务。我们将说明如何在群集环境中对应用程序轻松执行性能检查,而无需更改应用程序代码。

三种群集技术

Microsoft 服务器提供了三种支持群集的技术: 网络负载平衡 (NLB)、组件负载平衡 (CLB) 和 Microsoft 群集服务 (MSCS)。

网络负载平衡

网络负载平衡充当前端群集,用于在整个服务器群集中分配传入的 IP 流量,是为电子商务 Web 站点实现增量可伸缩性和出色可用性的理想选择。 最多可以将 32 个运行 Windows Server 2003 系列产品的计算机连接在一起共享一个虚拟 IP 地址。NLB 通过在群集内的多个服务器之间分配其客户端请求来增强可伸缩性。随着流量的增加,可以向群集添加更多的服务器,任何一个群集最多可容纳 32 个服务器。NLB 在为用户提供连续服务的同时还提供了高可用性,即自动检测服务器故障,并在 10 秒内在其余服务器中重新分配客户端流量。

组件负载平衡

组件负载平衡可以在多个运行站点业务逻辑的服务器之间分配负载。它在最多包含八个等同服务器的服务器群集中实现了 COM+ 组件的动态平衡。在 CLB 中,COM+ 组件位于单独的 COM+ 群集中的服务器上。激活 COM+ 组件的调用是平衡到 COM+ 群集中的不同服务器的负载。CLB 通过作用于多层群集网络的中间层与 NLB 和群集服务配合工作。 CLB 是作为 Application Center 2000 的特性提供的,可与 Microsoft 群集服务在同一组计算机上运行。

群集服务

群集服务充当后端群集,可为数据库、消息传递以及文件和打印服务等应用程序提供高可用性。当任一节点(群集中的服务器)发生故障或脱机时,MSCS 将尝试最大程度地减少故障对系统的影响。

1. 三种支持群集的 Microsoft 服务器技术

通过 Microsoft 群集服务实现故障转移

MSCS 故障转移功能是通过群集中连接的多个计算机中的冗余实现的,每台计算机都具有独立的故障状态。为了实现冗余,需要在群集中的多个服务器上安装应用程序。但在任一时刻,应用程序只在一个节点上处于联机状态。当该应用程序出现故障或该服务器停机时,此应用程序将在另一个节点上重新启动。 Windows Server 2003 数据中心版支持在一个群集中最多包含 8 个节点。

每个节点都具有自己的内存、系统磁盘、操作系统和群集资源的子集。如果某一节点出现故障,另一个节点将接管故障节点的资源(此过程称为“故障转移”)。然后,Microsoft 群集服务将在新节点上注册资源的网络地址,以便将客户端流量路由至当前拥有该资源的可用系统。当故障资源恢复联机状态时,MSCS 可配置为适当地重新分配资源和客户端请求(此过程称为“故障恢复”)。要使应用程序恢复到发生故障转移时的那一点,节点必须能够访问保持应用程序状态的共享存储区。

请注意,Microsoft 群集服务旨在提供高可用性,而不是真正的容错功能。“容错”一词通常用于描述提供更高级别恢复功能的技术。容错服务器通常使用结合了特定软件的高级硬件或数据冗余,提供从单个硬件或软件故障近乎瞬时的恢复。这类解决方案的成本远远高于群集解决方案,因为您必须购买冗余硬件,而冗余硬件只不过闲置在那里用于故障恢复。Microsoft 群集服务使用价格适宜的标准硬件提供出色的高可用性解决方案,同时最大程度地利用计算资源。

Microsoft 群集服务基于无共享的群集模型。无共享模型规定,虽然群集中有多个节点可以访问设备或资源,但该资源在一个时刻只能由一个系统占有和管理。(在 MSCS 群集中,资源是指任何可以联机或脱机、可在群集中进行管理、一个时刻只能以一个节点作为宿主并可以在节点之间移动的物理组件或逻辑组件。)

2. Microsoft 群集服务

群集服务结构

Microsoft 群集服务由三个主要组件构成:群集服务、资源监视器和资源 DLL。此外,还可以利用群集管理器创建提供管理功能的扩展 DLL。

群集服务

群集服务是核心组件,并作为高优先级的系统服务运行。群集服务控制群集活动并执行如下任务:协调事件通知、加速群集组件之间的通信、处理故障转移操作和管理配置。 每个群集节点都运行自己的群集服务。

资源监视器

资源监视器是群集服务和群集资源之间的接口,并作为独立进程运行。群集服务使用资源监视器与资源 DLL 进行通信。DLL 处理所有与资源的通信,因此 DLL 以资源监视器为宿主可以保护群集服务免受运行不正常或停止工作的资源造成的影响。资源监视器的多个副本可以在单个节点上运行,从而可以将无法预测的资源与其他资源隔离开。

群集服务在需要对资源执行操作时,向分配给该资源的资源监视器发送请求。如果资源监视器的进程中没有可以处理该类型资源的 DLL,则使用注册信息加载与该资源类型相关联的 DLL。然后,将群集服务的请求传递至其中一个 DLL 的入口点函数。资源 DLL 将处理操作的细节,以满足资源的特定需要。

资源 DLL

第三个关键的 Microsoft 群集服务组件是资源 DLL。资源监视器和资源 DLL 使用资源 API 进行通信。资源 API 是用于管理资源的入口点、回调函数和相关结构及宏的集合。

对于群集服务而言,资源是任何可进行管理的物理组件或逻辑组件,例如磁盘、网络名、IP 地址、数据库、Web 站点、应用程序以及任何其他可以联机和脱机的实体。资源可按类型进行组织。资源类型包括物理硬件(例如磁盘驱动器)和逻辑项(例如 IP 地址、文件共享和一般应用程序)。

每个资源都使用资源 DLL,它主要是资源监视器和资源之间的被动转换层。资源监视器调用资源 DLL 的入口点函数来查看资源的状态,使资源联机和脱机。资源 DLL 负责通过任何方便的 IPC 机制与其资源进行通信,以实现这些方法。

实现其自身资源 DLL 与群集服务通信的应用程序以及使用群集 API 请求和更新群集信息的应用程序都被定义为识别群集的应用程序。不使用群集或资源 API 以及群集控制代码函数的应用程序和服务都不识别群集,也不知道群集服务在运行。这些不识别群集的应用程序通常作为一般应用程序或服务进行管理。

识别群集的应用程序和不识别群集的应用程序都可以在群集节点上运行,并且都可以作为群集资源进行管理。但是,只有识别群集的应用程序可以利用群集服务通过群集 API 提供的功能。开发识别群集的应用程序需要建立自定义资源类型。通过自定义资源类型,开发人员可以使其应用程序在群集内发生各种事件(例如,节点即将脱机,因此会关闭数据库连接)时,作出响应并采取必要的措施。

对于大多数需要在群集中运行的应用程序,最好投入一些时间和资源开发自定义资源类型。不过,可以先在群集环境中对应用程序进行测试,而不必修改应用程序的代码或创建新的资源类型。在 Windows Server 2003 系列中,未经修改的应用程序可以作为“不识别群集的”应用程序以基本级别运行。群集服务专为此用途提供了一般应用程序资源类型。

群集管理器扩展 DLL

群集管理器扩展 DLL 在群集管理器内提供特定于应用程序的管理功能,允许用户以同样的方式管理他们的应用程序,无论该应用程序是在群集内部运行还是在群集外部运行。开发人员可以在群集管理器的框架内提供应用程序管理功能,或只是将这些功能链接到现有的管理工具。

开发人员可以通过编写扩展 DLL 来扩展群集管理器的功能。群集管理器应用程序通过一组已定义的 COM 接口与扩展 DLL 进行通信。扩展 DLL 必须实现一组特定的接口并且在群集的每个节点上都进行注册。

3. 关键组件: 群集服务、资源监视器和资源 DLL

不识别群集的应用程序

不提供其自身资源 DLL 的应用程序或服务仍可以在群集环境中进行配置。Windows Server 2003 系列中的群集服务包括仅用于此目的的一般资源 DLL:一般应用程序资源 DLL 和一般服务资源 DLL。群集服务将这些应用程序或服务看作是不识别群集的一般应用程序或服务。

一般资源 DLL 只提供最基本的控制。例如,一般应用程序资源 DLL 通过确定应用程序的进程是否仍然存在来检查应用程序是否发生故障,并通过终止进程使应用程序脱机。但它并不依赖于其他资源,而是提供一个在群集环境中测试应用程序的简单方法。

高可用性记事本

并非所有应用程序都能在群集中高效地工作。最有效的评估方式就是在群集中实际部署应用程序。执行初次测试的最简单机制是使用内置的一般应用程序资源类型将应用程序登记到群集中。“Generic Application”(一般应用程序)资源类型作为 Windows Server 2003 系列中群集服务的一部分提供,可以通过查看 “Cluster Administrator”(群集管理器)工具中 “Cluster Configuration”(群集配置)下的 “Resource Types”(资源类型)节点查看该类型以及其他内置资源类型(参见图 4)。

4. 群集管理器工具中的资源类型节点

群集管理器具有交互式向导,使您能够为列出的任何资源类型创建资源。群集服务还提供了 COM 接口,您可以用编程的方式创建和管理资源。

最新的群集管理器工具及相关的开发资源可以从 Platform SDK 获得。

群集自动化服务器

群集自动化服务器提供一组自动化对象,用于向脚本语言公开一个完整的群集管理接口,使您能够开发基于 Web 的远程管理工具。群集自动化服务器能够简化和增强创建群集管理应用程序的过程。

群集自动化服务器的本质是面向对象的,这意味着几乎所有群集编程任务都会涉及以下步骤:

  • 确定需要执行的群集操作。

  • 查找具有属性或方法的群集自动化服务器对象以完成操作。

  • 确定如何获取步骤 2 中的对象。MSDN® 中提供的对象层次结构可用于此目的。

  • 获取对象并调用属性或方法。

为了进行说明,我们将使用 Windows Scripting Host 和 Microsoft VBScript 以编程方式创建一般应用程序资源。

群集对象

群集对象是顶层对象,允许创建新实例。群集对象的 ProgID 是 “MSCLUSTER.CLUSTER”:

    Set oCluster = CreateObject( "MSCluster.Cluster" )

打开群集

在使用群集上的任何方法之前,必须先打开到该群集的连接。Open 方法可打开到群集的连接。将空字符串 ("") 作为参数传递给 Open 方法,即可打开到 localhost 上的群集的连接。脚本将在本地主机服务器上运行:

    oCluster.Open( "" )

创建组

群集组是群集资源的容器。当组中的一个资源发生故障且必须将该资源转移到其他节点时,该组中的所有资源都将被移动。组还定义了依赖关系边界。某个资源只能建立与另一个资源的依赖关系,前提是,该资源在同一个组中。为了进行测试,我们将创建一个独特的组,名为 “High Availability Notepad”(高可用性记事本):

    Set oGroup = oCluster.ResourceGroups.CreateItem( "High Availability
      Notepad" )

创建资源

每个组都包含资源集合。CreateItem 方法创建一个新资源,并将其添加到组的集合中。在本示例中,我们将创建一个名为 “Notepad” 的资源,资源类型为 “Generic Application”:

    Set oResource = oGroupResources.CreateItem( "Notepad", "Generic 
      Application", 0 )

设置资源属性

每个一般应用程序资源都具有两个可使资源联机的基本属性: CommandLine 和 CurrentDirectory。CommandLine 包含资源联机时要执行的命令,而 CurrentDirectory 则指定从中执行命令的文件系统目录。当此脚本执行语句使资源联机时,将启动 Notepad。要查看 Notepad,我们还需要将 InteractWithDesktop 属性设置为 1。

    Set oProperties = oResource.PrivateProperties

    ' Set the properties of the Generic Application
    oProperties.Item("CommandLine") = "notepad" 
    oProperties.Item("CurrentDirectory") = "c:/"
    oProperties.Item("InteractWithDesktop") = 1

    oProperties.SaveChanges

使资源联机

Online 方法使资源联机。Online 是一种状态,用于描述资源对群集可用。对于一般应用程序,使资源联机就意味着启动 Notepad。

    oResource.Online 10

完整的脚本清单

Option Explicit

Main
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 
  Main subroutine.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  Sub Main

    Dim oGroup
    Dim oCluster
    Dim oResource

    ' Create the Cluster object.
    Set oCluster = CreateObject( "MSCluster.Cluster" )

    ' Open the cluster. Empty string means open the local cluster.
    oCluster.Open( "" )

    ' Create or open the group.
    AddGroup oCluster, oGroup

    ' Create or open the resource.
    AddResource oGroup, oResource

    ' Bring the resource online and wait for up to 10 seconds
    ' for it to come online.
    oResource.Online 10

End Sub

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' This subroutine will create or open the group.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub AddGroup( oCluster, oGroup )
    Set oGroup = oCluster.ResourceGroups.CreateItem( "High Availability 
      Notepad" )
End Sub

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 
  This subroutine will add the resource to the group.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub AddResource( oGroup, oResource )
    Dim oGroupResources
    Dim oProperties
    Dim oCLProperty
    Dim oCDPropery

    Set oGroupResources = oGroup.Resources
    Set oResource = oGroupResources.CreateItem( "Notepad", "Generic 
      Application", 0 ) 'CLUSTER_RESOURCE_DEFAULT_MONITOR
    Set oProperties = oResource.PrivateProperties

    ' Set the properties of the Generic Application
    oProperties.Item("CommandLine") = "notepad" 
    oProperties.Item("CurrentDirectory") = "c:/"
    oProperties.Item("InteractWithDesktop") = 1

    oProperties.SaveChanges
End Sub

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Wait for a specified number of time.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub Sleep( PauseTime )
    Dim Start

    Start = Timer

    Do While Timer < Start + PauseTime
    Loop

End Sub

通过运行这个简单的脚本,将创建新的 “Notepad” 资源,并将其放入自身的组(“High Availability Notepad”)中。

验证结果

我们可以使用群集管理器来验证结果。通过查看“群集管理器”中 Notepad 资源的属性,可以看到已经正确设置了参数(见图 5)。

5. Notepad 资源参数

查看 “Advanced”(高级)选项卡,您将看到默认属性,指示发生故障时应用程序至多重新启动 3 次。LooksAlive 和 IsAlive 轮询间隔默认为资源类型中的值,但也可以通过指定其他值将其改写。由于此应用程序没有特殊代码使其成为识别群集的应用程序,因此仅通过在系统中运行的进程存在与否确定它是否处于活动状态。

6. Notepad 资源 “Advanced” 选项卡

测试应用程序

Notepad 资源联机后将在服务器上启动。如果 Notepad 进程被终止,它将立即再次启动。这是因为群集服务在发挥作用,试图使应用程序保持打开和运行状态。作为一般应用程序资源,无论什么时候,只要应用程序进程不再运行,群集服务就能够立即注意到,并根据策略采取纠正措施。

如果应用程序发生的故障不会导致进程终止(例如,网络故障、挂起或后台线程终止),结果会怎样呢?遗憾的是,对于一般应用程序资源类型,您只能进行一般故障检测。大多数编写在群集环境中运行的应用程序的开发人员都倾向于生成自定义资源 DLL,来处理与应用程序有关的问题。要不是作为在群集中评估应用程序的快捷方法,一般应用程序资源类型不会得到应用。

结论

Microsoft 群集服务使用价格适宜的标准硬件提供高可用性,同时最大程度地利用计算资源。Windows Server 2003 系列中的群集服务提供了强大的工具,使您的应用程序具有较高的可用性。对于某些开发人员而言,编写识别群集的应用程序可能要付出很大的代价,且难度较大。为了使开发人员能够以较低的投入来使用集群,群集服务提供了一般应用程序资源类型,允许在群集内对应用程序进行简单配置。虽然一般应用程序资源类型可能不会提供生产型应用程序所需的复杂性,但它提供了一种测试方法,可以查看应用程序在群集内的执行情况。

转入原英文页面

#include <signal.h> #include <getopt.h> #include <ethernet.h> #include <bcmeth.h> #include <bcmevent.h> #include <802.11.h> #include <bcmiov.h> #include "qm_utils.h" #include "qm_mscs.h" #ifdef BCM_QOSMGMT_MULTIAP #include "qm_mesh.h" #endif /* BCM_QOSMGMT_MULTIAP */ #define MSCS_COMMAND_LEN (SYS_CMD_LEN + 16) /* Assume by default AC_VO and AC_VI needs mscs processing. Corresponding up bitmap is 0xF0 */ #define QOSMGMT_DEFAULT_UP_BITMAP 0xF0 #define QOSMGMT_DEFAULT_UP_LIMIT 7 static bool qosmgmt_is_mscs_disallowed(qosmgmt_t *qosmgmt, const uint8 *da); static void qosmgmt_mscs_reset_kernel() { char command[SYS_CMD_LEN]; ASSERT(qm_sta_list.mscs_sta_count == 0); if (qm_sta_list.scs_sta_count_tclas == 0) { if (netfilter_module) { snprintf(command, SYS_CMD_LEN, "rmmod br_netfilter.ko &> /dev/null"); SYSTEM_CALL(command); ASSERT(netfilter_loaded == TRUE); netfilter_loaded = FALSE; } iptables_deinit(); } } static void qosmgmt_mscs_init_kernel() { char command[SYS_CMD_LEN]; ASSERT(qm_sta_list.mscs_sta_count == 0); if (qm_sta_list.scs_sta_count_tclas == 0) { if (netfilter_module) { /* Insert br_nfilter module and set global mscs iptables rule */ snprintf(command, SYS_CMD_LEN, "insmod /lib/modules/$(uname -r)/" "kernel/net/bridge/br_netfilter.ko &> /dev/null"); SYSTEM_CALL(command); ASSERT(netfilter_loaded == FALSE); netfilter_loaded = TRUE; } iptables_init(); } } static int qosmgmt_delete_mscs_stalist(qosmgmt_t *qosmgmt, char *ifname, const uint8 *da) { actframe_entry_t *entry = NULL; actframe_entry_t *next = NULL; entry = qosmgmt->head; while (entry) { next = entry->next; if ((entry->type == QOSMGMT_MSCS) && (strncmp(entry->ifname, ifname, IFNAMSIZ) == 0) && (!eacmp(entry->stamac.octet, da))) { /* delete this entry */ qosmgmt_delete_actframe_entry(qosmgmt, ifname, (char *)&entry->stamac, entry->type); /* only one mscs entry per sta */ break; } entry = next; } return BCME_OK; } static int qosmgmt_update_mscs_response_actframe(qosmgmt_t *qosmgmt, actframe_entry_t *entry, dot11_mscs_req_t *req_act_fr) { dot11_mscs_descr_ie_t *mscs_descr_ie = NULL; dot11_mscs_res_t *mscs_res = NULL; char *ioctl_buf = NULL; wl_af_params_t *af_params; wl_action_frame_t *action_frame; int buflen; if (entry == NULL) { QOSMGMT_ERROR("Error: entry is NULL\n"); return BCME_ERROR; } ASSERT(entry->actframe_buflen >= WLC_IOCTL_SMLEN); ioctl_buf = (char *)entry->actframe_buf; QOSMGMT_INFO("Action Frame cat: %d rav: %d, dia: %d\n", req_act_fr->category, req_act_fr->robust_action, req_act_fr->dialog_token); mscs_descr_ie = &req_act_fr->mscs_descr; /* strncpy will pad ioctl_buf with 0 after "actframe" until * WLC_IOCTL_SMLEN, so no need to memset with 0 and fill last * chat to '\0'. */ strncpy(ioctl_buf, "actframe", WLC_IOCTL_SMLEN); buflen = strlen(ioctl_buf) + 1; af_params = (wl_af_params_t *)(ioctl_buf + buflen); action_frame = &af_params->action_frame; af_params->channel = 0; af_params->dwell_time = htod32(-1); eacopy(entry->stamac.octet, action_frame->da.octet); action_frame->packetId = entry->packetId; action_frame->len = htod16(DOT11_MSCS_RES_HDR_LEN); mscs_res = (dot11_mscs_res_t *)&action_frame->data[0]; mscs_res->category = DOT11_ACTION_CAT_QOSMGMT; mscs_res->robust_action = DOT11_QOSMGMT_MSCS_RES; mscs_res->dialog_token = req_act_fr->dialog_token; if (qosmgmt_is_mscs_disallowed(qosmgmt, entry->stamac.octet)) { mscs_res->status = DOT11_SC_DECLINED; } else if ((mscs_descr_ie->req_type == DOT11_MSCS_REQ_TYPE_ADD) || (mscs_descr_ie->req_type == DOT11_MSCS_REQ_TYPE_CHANGE)) { mscs_res->status = DOT11_SC_SUCCESS; } else { /* WFA #4.4.3.5 - * when AP receives MSCS Request with Request Type subfield as 1 (REMOVE) * MSCS Response should go with Status Code TCLAS_PROCESSING_TERMINATED (97) */ mscs_res->status = DOT11_SC_TCLAS_PROCESSING_TERMINATED; } return mscs_res->status; } static int qosmgmt_update_mscs_terminate_actframe(qosmgmt_t *qosmgmt, actframe_entry_t *entry) { dot11_mscs_res_t *mscs_res = NULL; char *ioctl_buf = NULL; wl_af_params_t *af_params; wl_action_frame_t *action_frame; int buflen; if (entry == NULL) { QOSMGMT_ERROR("%s:Error: entry is NULL\n", __FUNCTION__); return BCME_ERROR; } ASSERT(entry->actframe_buflen >= WLC_IOCTL_SMLEN); ioctl_buf = (char *)entry->actframe_buf; strncpy(ioctl_buf, "actframe", WLC_IOCTL_SMLEN); buflen = strlen(ioctl_buf) + 1; af_params = (wl_af_params_t *)(ioctl_buf + buflen); action_frame = &af_params->action_frame; af_params->channel = 0; af_params->dwell_time = htod32(-1); eacopy(entry->stamac.octet, action_frame->da.octet); action_frame->packetId = entry->packetId; action_frame->len = htod16(DOT11_MSCS_RES_HDR_LEN); /* WFA #4.4.2.9 - Trigger APUT to delete (teardown) MSCS for STA2 */ mscs_res = (dot11_mscs_res_t *)&action_frame->data[0]; mscs_res->category = DOT11_ACTION_CAT_QOSMGMT; mscs_res->robust_action = DOT11_QOSMGMT_MSCS_RES; mscs_res->dialog_token = 0; mscs_res->status = DOT11_SC_TCLAS_PROCESSING_TERMINATED; return BCME_OK; } static int qosmgmt_update_mscs_descr_ie(qosmgmt_t *qosmgmt, qm_sta_entry_t *sta_e, void *data, uint32 datalen, uint32 req_type) { #ifdef BCM_QOSMGMT_MULTIAP uint8 *descr_ie = NULL; if (!I5_IS_MULTIAP_CONTROLLER(qosmgmt->multiap_mode)) { return BCME_OK; } if (req_type == DOT11_MSCS_REQ_TYPE_ADD) { ASSERT(data && datalen); descr_ie = (uint8 *)calloc(1, datalen); if (descr_ie == NULL) { QOSMGMT_ERROR("can't allocate memory\n"); return BCME_NOMEM; } memcpy(descr_ie, data, datalen); sta_e->mscs_param.mscs_ie.descr_ie = descr_ie; sta_e->mscs_param.mscs_ie.descr_len = datalen; } else if (req_type == DOT11_MSCS_REQ_TYPE_REMOVE) { if (sta_e->mscs_param.mscs_ie.descr_ie) { free(sta_e->mscs_param.mscs_ie.descr_ie); sta_e->mscs_param.mscs_ie.descr_ie = NULL; sta_e->mscs_param.mscs_ie.descr_len = 0; } } else { ASSERT(FALSE); } #endif /* BCM_QOSMGMT_MULTIAP */ return BCME_OK; } int qosmgmt_iptables_mscs_cmd(qosmgmt_t *qosmgmt, qm_sta_entry_t *e, iptables_entry_type_t type) { char macstr[ETHER_ADDR_STR_LEN]; char command[MSCS_COMMAND_LEN]; char cmd_postfix[SYS_CMD_LEN]; int ret = BCME_OK; ether_etoa((uint8 *)&e->ea, macstr); if (type == IPTABLES_ADD) { if (qm_sta_list.mscs_sta_count == 0) { /* This will be first enrty. Initialize kernel and netfilter */ qosmgmt_mscs_init_kernel(); } } snprintf(cmd_postfix, SYS_CMD_LEN, "-t mangle %s MSCS-RULES -m mac --mac-source %s -m mscs" " --up-bitmap 0x%x --up-limit %d -j RETURN", type == IPTABLES_ADD ? "-A":"-D", macstr, e->mscs_param.up_bitmap, e->mscs_param.up_limit); snprintf(command, MSCS_COMMAND_LEN, "iptables %s", cmd_postfix); SYSTEM_CALL(command); snprintf(command, MSCS_COMMAND_LEN, "ip6tables %s", cmd_postfix); SYSTEM_CALL(command); if (type == IPTABLES_DEL) { /* delete stored mscs_desc_ie */ qosmgmt_update_mscs_descr_ie(qosmgmt, e, NULL, 0, DOT11_MSCS_REQ_TYPE_REMOVE); if (qm_sta_list.mscs_sta_count == 0) { /* Left last MSCS station. Return to default kernel mode */ qosmgmt_mscs_reset_kernel(); } if (ASR_ENAB(e->sta_cap)) { if (qosmgmt_mscs_external_add(qosmgmt, e)) { /* A replacement entry is added by asr auto mscs */ return BCME_OK; } } } if (COMMAND_VALID("fc") == TRUE) { /* Evict all existing flows of the station */ snprintf(command, MSCS_COMMAND_LEN, "fc flush --mac %s &> /dev/null", macstr); SYSTEM_CALL(command); } else if (DIRECTORY_VALID("/proc/driver/fpi_blog")) { /* flush all FPI flows */ snprintf(command, MSCS_COMMAND_LEN, "echo 1 > /proc/driver/fpi_blog/flush"); SYSTEM_CALL(command); } return ret; } bool qosmgmt_mscs_external_add(qosmgmt_t *qosmgmt, qm_sta_entry_t *e) { uint32 qosmgmt_enable; /* MSCS entry for the station will be necessary on following conditions: * 1. If the AP's qosmgmt module is enabled for MSCS and ASR_AUTO_MSCS * 2. If the station does not have already a MSCS entry which may have been * from ASR session or from a MSCS request */ if (MSCS_ENAB(e->sta_cap)) { /* MSCS is already active in this station. */ return FALSE; } qosmgmt_enable = qosmgmt_get_iface_enable(qosmgmt, e->ifname); if (!(MSCS_ENAB(qosmgmt_enable) && ASR_AUTO_MSCS_ENAB(qosmgmt_enable))) { /* MSCS is not enabled. OR it is enabled but auto mscs is not enabled */ return FALSE; } e->sta_cap |= WL_QOSMGMT_ASR_AUTO_MSCS; e->sta_cap |= WL_QOSMGMT_MSCS; e->mscs_param.up_bitmap = QOSMGMT_DEFAULT_UP_BITMAP; e->mscs_param.up_limit = QOSMGMT_DEFAULT_UP_LIMIT; e->mscs_param.stream_timeout = -1; /* Not used */ e->mscs_param.external_request = TRUE; qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_ADD); qm_sta_list.mscs_sta_count++; return TRUE; } bool qosmgmt_mscs_external_entry_delete(qosmgmt_t *qosmgmt, qm_sta_entry_t *e) { if (!MSCS_ENAB(e->sta_cap)) { return FALSE; } if (!e->mscs_param.external_request) { /* This entry is from MSCS request. Don't delete it */ return FALSE; } /* clear MSCS cap */ e->sta_cap &= (~WL_QOSMGMT_MSCS); e->sta_cap &= (~WL_QOSMGMT_ASR_AUTO_MSCS); e->mscs_param.external_request = FALSE; qm_sta_list.mscs_sta_count--; /* Delete associated iptables entry */ qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_DEL); return TRUE; } int qosmgmt_process_mscs_ie(qosmgmt_t *qosmgmt, char *ifname, void *data, uint32 datalen, const uint8 *da) { dot11_mscs_descr_ie_t *mscs_descr_ie; uint8 up_bitmap, up_limit; qm_sta_entry_t *e; char macstr[ETHER_ADDR_STR_LEN]; if (datalen < sizeof(*mscs_descr_ie)) { return BCME_BADARG; } mscs_descr_ie = (dot11_mscs_descr_ie_t *)data; ether_etoa(da, macstr); up_bitmap = (mscs_descr_ie->up_ctl & DOT11_UPC_UP_BITMAP_MASK) >> DOT11_UPC_UP_BITMAP_SHIFT; up_limit = (mscs_descr_ie->up_ctl & DOT11_UPC_UP_LIMIT_MASK) >> DOT11_UPC_UP_LIMIT_SHIFT; QOSMGMT_INFO("MSCS IE [%s] type: %d, up_bitmap: 0x%x, up_limit: %d timeout: %d\n", macstr, mscs_descr_ie->req_type, up_bitmap, up_limit, mscs_descr_ie->stream_timeout); if (mscs_descr_ie->req_type > DOT11_MSCS_REQ_TYPE_CHANGE) { QOSMGMT_ERROR("Error: Invalid request type : %d from %s\n", mscs_descr_ie->req_type, macstr); return BCME_ERROR; } e = qosmgmt_get_sta_entry(da, ifname, TRUE); if (e == NULL) { QOSMGMT_ERROR("Out of memory.\n"); return BCME_NOMEM; } if (mscs_descr_ie->req_type == DOT11_MSCS_REQ_TYPE_REMOVE) { if (MSCS_ENAB(e->sta_cap)) { /* clear MSCS cap */ e->sta_cap &= (~WL_QOSMGMT_MSCS); qm_sta_list.mscs_sta_count--; /* Delete associated iptables entry */ qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_DEL); } } else { if (MSCS_ENAB(e->sta_cap)) { /* Delete associated iptable entry */ qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_DEL); } /* replace param */ e->mscs_param.up_bitmap = up_bitmap; e->mscs_param.up_limit = up_limit; e->mscs_param.stream_timeout = mscs_descr_ie->stream_timeout; e->mscs_param.external_request = FALSE; /* This entry is from a MSCS action frame */ e->sta_cap &= (~WL_QOSMGMT_ASR_AUTO_MSCS); qosmgmt_update_mscs_descr_ie(qosmgmt, e, data, datalen, DOT11_MSCS_REQ_TYPE_ADD); /* Add new iptables entry */ qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_ADD); if (!MSCS_ENAB(e->sta_cap)) { e->sta_cap |= WL_QOSMGMT_MSCS; qm_sta_list.mscs_sta_count++; } } /* Free station entry if sta_cap is zero */ qosmgmt_free_sta_entry(e->ea.octet); return BCME_OK; } static int qosmgmt_mscs_iptables_delete(qosmgmt_t *qosmgmt, const uint8 *da) { qm_sta_entry_t *e; char macstr[ETHER_ADDR_STR_LEN]; ether_etoa(da, macstr); e = qosmgmt_get_sta_entry(da, NULL, FALSE); if (e == NULL) { /* No entry for this station */ QOSMGMT_INFO("No entry for STA: %s\n", macstr); return BCME_OK; } if (!MSCS_ENAB(e->sta_cap)) { QOSMGMT_INFO("STA : %s is not MSCS capable\n", macstr); return BCME_OK; } /* clear MSCS cap */ e->sta_cap &= (~WL_QOSMGMT_MSCS); qm_sta_list.mscs_sta_count--; /* Delete associated iptables entry */ qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_DEL); /* Free station entry if sta_cap is zero */ qosmgmt_free_sta_entry(e->ea.octet); return BCME_OK; } int qosmgmt_process_mscs_request(qosmgmt_t *qosmgmt, char *ifname, void *data, uint32 datalen, const uint8 *da) { dot11_mscs_req_t *req_act_fr; dot11_mscs_descr_ie_t *mscs_descr_ie; actframe_entry_t *entry; int ret = BCME_OK; if (datalen < sizeof(*req_act_fr)) { return BCME_BADARG; } req_act_fr = (dot11_mscs_req_t *)data; mscs_descr_ie = &req_act_fr->mscs_descr; datalen -= OFFSETOF(dot11_mscs_req_t, mscs_descr); /* delete any pending MSCS resp actframe */ qosmgmt_delete_mscs_stalist(qosmgmt, ifname, da); /* allocate entry for MSCS resp actframe */ entry = qosmgmt_add_actframe_entry(qosmgmt, ifname, da, QOSMGMT_MSCS); /* update MSCS resp actframe buffer for each sta */ ret = qosmgmt_update_mscs_response_actframe(qosmgmt, entry, req_act_fr); if (ret != DOT11_SC_DECLINED) { /* add mscs iptables rules */ qosmgmt_process_mscs_ie(qosmgmt, ifname, mscs_descr_ie, datalen, da); #ifdef BCM_QOSMGMT_MULTIAP qosmgmt_send_wbd_ipc_mscs_success(qosmgmt, mscs_descr_ie, datalen, (uchar *)da); #endif /* BCM_QOSMGMT_MULTIAP */ } /* dump all sta in the list of type MSCS */ qosmgmt_dump_actframe_list(qosmgmt, ifname, QOSMGMT_MSCS); /* transmit MSCS resp actframe */ qosmgmt_send_actionframe_entry(qosmgmt, entry); return BCME_OK; } int qosmgmt_process_mscs_assoc(qosmgmt_t *qosmgmt, char *ifname, void *data, uint32 datalen, const uint8 *da) { if (qosmgmt_is_mscs_disallowed(qosmgmt, da)) { actframe_entry_t *entry; /* delete any pending MSCS actframe */ qosmgmt_delete_mscs_stalist(qosmgmt, ifname, da); /* allocate entry for MSCS terminate actframe */ entry = qosmgmt_add_actframe_entry(qosmgmt, ifname, da, QOSMGMT_MSCS); /* update MSCS terminate actframe buffer for sta */ qosmgmt_update_mscs_terminate_actframe(qosmgmt, entry); /* dump all sta in the list of type MSCS */ qosmgmt_dump_actframe_list(qosmgmt, ifname, QOSMGMT_MSCS); /* transmit MSCS terminate actframe */ qosmgmt_send_actionframe_entry(qosmgmt, entry); } else { /* add mscs iptables rules */ qosmgmt_process_mscs_ie(qosmgmt, ifname, data, datalen, da); #ifdef BCM_QOSMGMT_MULTIAP qosmgmt_send_wbd_ipc_mscs_success(qosmgmt, data, datalen, (uchar *)da); #endif /* BCM_QOSMGMT_MULTIAP */ } return BCME_OK; } int qosmgmt_process_mscs_terminate(qosmgmt_t *qosmgmt, char *ifname, const uint8 *da) { actframe_entry_t *entry; /* delete any pending MSCS actframe */ qosmgmt_delete_mscs_stalist(qosmgmt, ifname, da); /* allocate entry for MSCS terminate actframe */ entry = qosmgmt_add_actframe_entry(qosmgmt, ifname, da, QOSMGMT_MSCS); /* update MSCS terminate actframe buffer for sta */ qosmgmt_update_mscs_terminate_actframe(qosmgmt, entry); /* delete mscs iptables rules */ qosmgmt_mscs_iptables_delete(qosmgmt, da); /* dump all sta in the list of type MSCS */ qosmgmt_dump_actframe_list(qosmgmt, ifname, QOSMGMT_MSCS); /* transmit MSCS terminate actframe */ qosmgmt_send_actionframe_entry(qosmgmt, entry); return BCME_OK; } #ifdef RESTORE_STA_MSCS static int qosmgmt_restore_sta_mscs_ie(qosmgmt_t *qosmgmt, char *ifname, const uint8 *mac) { int ret = BCME_OK; bcm_iov_buf_t *iov_buf = NULL; bcm_iov_buf_t *p_resp = NULL; uint8 *pxtlv = NULL; uint16 buflen = 0, buflen_start = 0; uint16 iovlen = 0; uint8 *iov_resp = NULL; int sbuflen; int hdr_size; iov_buf = (bcm_iov_buf_t *)calloc(1, WLC_IOCTL_MEDLEN); if (iov_buf == NULL) { return BCME_NOMEM; } iov_resp = (uint8 *)calloc(1, WLC_IOCTL_MAXLEN); if (iov_resp == NULL) { ret = BCME_NOMEM; goto fail; } /* fill header */ iov_buf->version = WL_QOSMGMT_IOV_VERSION; iov_buf->id = WL_QOSMGMT_CMD_DESC_IE; pxtlv = (uint8 *)&iov_buf->data[0]; buflen = buflen_start = WLC_IOCTL_MEDLEN - sizeof(bcm_iov_buf_t); ret = bcm_pack_xtlv_entry(&pxtlv, &buflen, WL_QOSMGMT_XTLV_MACADDR, ETHER_ADDR_LEN, (uint8*)mac, BCM_XTLV_OPTION_ALIGN32); if (ret != BCME_OK) { goto fail; } iov_buf->len = buflen_start - buflen; iovlen = sizeof(bcm_iov_buf_t) + iov_buf->len; ret = wl_iovar_getbuf(ifname, "qosmgmt", iov_buf, iovlen, iov_resp, WLC_IOCTL_MAXLEN); if (ret != BCME_OK) { goto fail; } p_resp = (bcm_iov_buf_t *)iov_resp; if (p_resp->id != WL_QOSMGMT_CMD_DESC_IE) { goto fail; } pxtlv = (uint8 *)p_resp->data; sbuflen = p_resp->len; hdr_size = BCM_XTLV_HDR_SIZE_EX(BCM_XTLV_OPTION_ALIGN32); while (sbuflen >= hdr_size) { uint16 type, len; int size; const bcm_xtlv_t *ptlv; const uint8 *data; ptlv = (const bcm_xtlv_t *)pxtlv; bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, BCM_XTLV_OPTION_ALIGN32); size = bcm_xtlv_size_for_data(len, BCM_XTLV_OPTION_ALIGN32); sbuflen -= size; if (sbuflen < 0) /* check for buffer overrun */ break; if (type == WL_QOSMGMT_XTLV_MSCS_IE) { /* update iptables based on mscs desc ie */ qosmgmt_process_mscs_ie(qosmgmt, ifname, data, len, mac); } else if (type == WL_QOSMGMT_XTLV_SCS_IE) { /* TODO: update iptables based on scs desc ie */ } pxtlv += size; } fail: if (iov_buf) { free(iov_buf); } if (iov_resp) { free(iov_resp); } return ret; } int qosmgmt_restore_mscs_ie(qosmgmt_t *qosmgmt, char *ifname) { int ret = BCME_OK; bcm_iov_buf_t *iov_buf = NULL; bcm_iov_buf_t *p_resp = NULL; uint8 *pxtlv = NULL; uint16 buflen = 0, buflen_start = 0; uint16 iovlen = 0; uint8 stacap = 0; uint8 *iov_resp = NULL; int sbuflen; int hdr_size; iov_buf = (bcm_iov_buf_t *)calloc(1, WLC_IOCTL_MEDLEN); if (iov_buf == NULL) { return BCME_NOMEM; } iov_resp = (uint8 *)calloc(1, WLC_IOCTL_MAXLEN); if (iov_resp == NULL) { ret = BCME_NOMEM; goto fail; } /* fill header */ iov_buf->version = WL_QOSMGMT_IOV_VERSION; iov_buf->id = WL_QOSMGMT_CMD_STALIST; pxtlv = (uint8 *)&iov_buf->data[0]; buflen = buflen_start = WLC_IOCTL_MEDLEN - sizeof(bcm_iov_buf_t); stacap = bcm_find_fsb(WL_QOSMGMT_MSCS) - 1; ret = bcm_pack_xtlv_entry(&pxtlv, &buflen, WL_QOSMGMT_XTLV_STACAP, sizeof(stacap), &stacap, BCM_XTLV_OPTION_ALIGN32); if (ret != BCME_OK) { goto fail; } iov_buf->len = buflen_start - buflen; iovlen = sizeof(bcm_iov_buf_t) + iov_buf->len; ret = wl_iovar_getbuf(ifname, "qosmgmt", iov_buf, iovlen, iov_resp, WLC_IOCTL_MAXLEN); if (ret != BCME_OK) { goto fail; } p_resp = (bcm_iov_buf_t *)iov_resp; if (p_resp->id != WL_QOSMGMT_CMD_STALIST) { goto fail; } pxtlv = (uint8 *)p_resp->data; sbuflen = p_resp->len; hdr_size = BCM_XTLV_HDR_SIZE_EX(BCM_XTLV_OPTION_ALIGN32); while (sbuflen >= hdr_size) { uint16 type, len; int size; const bcm_xtlv_t *ptlv; const uint8 *data; ptlv = (const bcm_xtlv_t *)pxtlv; bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, BCM_XTLV_OPTION_ALIGN32); size = bcm_xtlv_size_for_data(len, BCM_XTLV_OPTION_ALIGN32); sbuflen -= size; if (sbuflen < 0) /* check for buffer overrun */ break; /* restore sta iptables */ qosmgmt_restore_sta_mscs_ie(qosmgmt, ifname, data); pxtlv += size; } fail: if (iov_buf) { free(iov_buf); } if (iov_resp) { free(iov_resp); } return ret; } #endif /* RESTORE_STA_MSCS */ void qosmgmt_clear_mscs_iptables_all_entries(qosmgmt_t *qosmgmt) { int i; qm_sta_entry_ll_t *sta_ll; qm_sta_entry_t *e; if (qm_sta_list.mscs_sta_count == 0) { /* Nothing to clear */ return; } /* Delete all mscs iptables entries created by this app. */ QOSMGMT_INFO("Delete all MSCS iptables entries\n"); for (i = 0; i < QM_HASH_TABLE_SIZE; i++) { sta_ll = qm_sta_list.hash_table[i]; while (sta_ll != NULL) { e = sta_ll->sta_e; if (MSCS_ENAB(e->sta_cap)) { /* clear MSCS cap */ e->sta_cap &= (~WL_QOSMGMT_MSCS); qm_sta_list.mscs_sta_count--; /* Delete MSCS iptables entry */ qosmgmt_iptables_mscs_cmd(qosmgmt, e, IPTABLES_DEL); } /* Free station entry if sta_cap is zero */ sta_ll = qosmgmt_free_sta_entry(e->ea.octet); } } if (qm_sta_list.mscs_sta_count != 0) { QOSMGMT_ERROR("MSCS Station list is corrupted\n"); ASSERT(0); } } static bool qosmgmt_is_mscs_disallowed(qosmgmt_t *qosmgmt, const uint8 *da) { bool ret = FALSE; char macaddr[ETHER_ADDR_STR_LEN], *macnext; struct ether_addr mac_addr; if (qosmgmt->mscs_disallowed_list == NULL) { return ret; } foreach(macaddr, qosmgmt->mscs_disallowed_list, macnext) { if (!ether_atoe(macaddr, (uchar *)&mac_addr)) { QOSMGMT_ERROR("Invalid mac address (%s)\n", macaddr); continue; } if (!eacmp(mac_addr.octet, da)) { QOSMGMT_INFO("MSCS is disallowed for STA:"MACF"\n", ETHER_TO_MACF(*(struct ether_addr *)da)); ret = TRUE; break; } } return ret; }解析一下这段代码
最新发布
09-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值