commit 7dd6d43954ad88bac552ee00238185cd94886d70 Author: vemax78 Date: Wed May 1 14:52:55 2013 +0200 First commit diff --git a/.autom4te.cfg b/.autom4te.cfg new file mode 100755 index 0000000..8e4f67e --- /dev/null +++ b/.autom4te.cfg @@ -0,0 +1,40 @@ +# SmartCAPWAP -- +# +# Copyright (C) 2012-2013 Massimo Vellucci +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program (see the file COPYING included with this +# distribution); if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +begin-language: "Autoheader-preselections" +args: --no-cache +end-language: "Autoheader-preselections" + +begin-language: "Automake-preselections" +args: --no-cache +end-language: "Automake-preselections" + +begin-language: "Autoreconf-preselections" +args: --no-cache +end-language: "Autoreconf-preselections" + +begin-language: "Autoconf-without-aclocal-m4" +args: --no-cache +end-language: "Autoconf-without-aclocal-m4" + +begin-language: "Autoconf" +args: --no-cache +end-language: "Autoconf" + + diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..88a84e5 --- /dev/null +++ b/.hgignore @@ -0,0 +1 @@ +build.sh diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..2e56fcd --- /dev/null +++ b/COPYING @@ -0,0 +1,149 @@ +SmartCAPWAP -- An Open Source CAPWAP WTP / AC + +Copyright (C) 2012-2013 Massimo Vellucci + +This distribution contains multiple components, some +of which fall under different licenses. By using SmartCAPWAP +or any of the bundled components enumerated below, you +agree to be bound by the conditions of the license for +each respective component. + +SmartCAPWAP license: +---------------- + + SmartCAPWAP is distributed under the GPL license version 2. + +Libconfig license: +------------ + + Libconfig is Copyright (C) Mark Lindner, + and is licensed under the GPL license version 2. + +OpenSSL License: +---------------- + + The OpenSSL toolkit stays under a dual license, i.e. both the conditions of + the OpenSSL License and the original SSLeay license apply to the toolkit. + See below for the actual license texts. Actually both licenses are BSD-style + Open Source licenses. In case of any license issues related to OpenSSL + please contact openssl-core@openssl.org. + +/* ==================================================================== + * Copyright (c) 1998-2003 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +GNU Public License (GPL) +------------------------ + + SmartCAWAP, Libconfig distributions are + licensed under the GPL version 2 (see COPYRIGHT.GPL). diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..078775c --- /dev/null +++ b/INSTALL @@ -0,0 +1,28 @@ +================================================== +HOW TO BUILD AND RUN SmartCAPWAP FOR LINUX SYSTEMS +================================================== + +NOTE: To run WTP you must have a wireless card that has Linux driver based on the + Generic IEEE 802.11 Networking Stack (mac80211). + + +HOW TO BUILD AC AND WTP +======================= + +Requirements +------------ + +* automake 1.9 or newer +* autoconf +* libtool +* openssl + +Build +----- + +Run: + + autoreconf -f -i + ./configure + make + make install diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..1ad0288 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,34 @@ +# SmartCAPWAP -- An Open Source CAPWAP WTP / AC +# +# Copyright (C) 2012-2013 Massimo Vellucci +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program (see the file COPYING included with this +# distribution); if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +AUTOMAKE_OPTIONS = foreign 1.9 +ACLOCAL_AMFLAGS = -I m4 + +MAINTAINERCLEANFILES = \ + config.log config.status \ + $(srcdir)/Makefile.in \ + $(srcdir)/config.h.in $(srcdir)/config.h.in~ $(srcdir)/configure \ + $(srcdir)/install-sh $(srcdir)/ltmain.sh $(srcdir)/missing \ + $(srcdir)/m4/libtool.m4 $(srcdir)/m4/lt~obsolete.m4 \ + $(srcdir)/m4/ltoptions.m4 $(srcdir)/m4/ltsugar.m4 \ + $(srcdir)/m4/ltversion.m4 \ + $(srcdir)/depcomp $(srcdir)/aclocal.m4 \ + $(srcdir)/config.guess $(srcdir)/config.sub + +SUBDIRS = build diff --git a/build/Makefile.am b/build/Makefile.am new file mode 100755 index 0000000..d534fd1 --- /dev/null +++ b/build/Makefile.am @@ -0,0 +1,31 @@ +# SmartCAPWAP -- An Open Source CAPWAP WTP / AC +# +# Copyright (C) 2012-2013 Massimo Vellucci +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program (see the file COPYING included with this +# distribution); if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +MAINTAINERCLEANFILES = \ + $(srcdir)/Makefile.in + +SUBDIRS = + +if BUILD_AC +SUBDIRS += ac +endif + +if BUILD_WTP +SUBDIRS += wtp +endif diff --git a/build/Makefile_common.am b/build/Makefile_common.am new file mode 100755 index 0000000..59a9438 --- /dev/null +++ b/build/Makefile_common.am @@ -0,0 +1,69 @@ +# SmartCAPWAP -- An Open Source CAPWAP WTP / AC +# +# Copyright (C) 2012-2013 Massimo Vellucci +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program (see the file COPYING included with this +# distribution); if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +capwap_SOURCES = \ + $(top_srcdir)/src/common/md5.c \ + $(top_srcdir)/src/common/capwap.c \ + $(top_srcdir)/src/common/capwap_network.c \ + $(top_srcdir)/src/common/capwap_protocol.c \ + $(top_srcdir)/src/common/capwap_logging.c \ + $(top_srcdir)/src/common/capwap_list.c \ + $(top_srcdir)/src/common/capwap_array.c \ + $(top_srcdir)/src/common/capwap_dtls.c \ + $(top_srcdir)/src/common/capwap_dfa.c \ + $(top_srcdir)/src/common/capwap_element.c \ + $(top_srcdir)/src/common/capwap_element_acdescriptor.c \ + $(top_srcdir)/src/common/capwap_element_discoverytype.c \ + $(top_srcdir)/src/common/capwap_element_wtpboarddata.c \ + $(top_srcdir)/src/common/capwap_element_wtpdescriptor.c \ + $(top_srcdir)/src/common/capwap_element_wtpframetunnelmode.c \ + $(top_srcdir)/src/common/capwap_element_wtpmactype.c \ + $(top_srcdir)/src/common/capwap_element_acname.c \ + $(top_srcdir)/src/common/capwap_element_controlipv4.c \ + $(top_srcdir)/src/common/capwap_element_controlipv6.c \ + $(top_srcdir)/src/common/capwap_element_location.c \ + $(top_srcdir)/src/common/capwap_element_wtpname.c \ + $(top_srcdir)/src/common/capwap_element_sessionid.c \ + $(top_srcdir)/src/common/capwap_element_ecnsupport.c \ + $(top_srcdir)/src/common/capwap_element_localipv4.c \ + $(top_srcdir)/src/common/capwap_element_localipv6.c \ + $(top_srcdir)/src/common/capwap_element_transport.c \ + $(top_srcdir)/src/common/capwap_element_mtudiscovery.c \ + $(top_srcdir)/src/common/capwap_element_vendorpayload.c \ + $(top_srcdir)/src/common/capwap_element_maximumlength.c \ + $(top_srcdir)/src/common/capwap_element_wtprebootstat.c \ + $(top_srcdir)/src/common/capwap_element_resultcode.c \ + $(top_srcdir)/src/common/capwap_element_returnedmessage.c \ + $(top_srcdir)/src/common/capwap_element_acipv4list.c \ + $(top_srcdir)/src/common/capwap_element_acipv6list.c \ + $(top_srcdir)/src/common/capwap_element_imageidentifier.c \ + $(top_srcdir)/src/common/capwap_element_radioadmstate.c \ + $(top_srcdir)/src/common/capwap_element_statisticstimer.c \ + $(top_srcdir)/src/common/capwap_element_acnamepriority.c \ + $(top_srcdir)/src/common/capwap_element_wtpstaticipaddress.c \ + $(top_srcdir)/src/common/capwap_element_timers.c \ + $(top_srcdir)/src/common/capwap_element_decrypterrorreportperiod.c \ + $(top_srcdir)/src/common/capwap_element_idletimeout.c \ + $(top_srcdir)/src/common/capwap_element_wtpfallback.c \ + $(top_srcdir)/src/common/capwap_element_radiooprstate.c \ + $(top_srcdir)/src/common/capwap_element_80211_wtpradioinformation.c + +if DEBUG_BUILD +capwap_SOURCES += $(top_srcdir)/src/common/capwap_debug.c +endif diff --git a/build/ac/Makefile.am b/build/ac/Makefile.am new file mode 100755 index 0000000..c3cfc7c --- /dev/null +++ b/build/ac/Makefile.am @@ -0,0 +1,64 @@ +# SmartCAPWAP -- An Open Source CAPWAP WTP / AC +# +# Copyright (C) 2012-2013 Massimo Vellucci +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program (see the file COPYING included with this +# distribution); if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +MAINTAINERCLEANFILES = \ + $(srcdir)/Makefile.in + +bin_PROGRAMS = ac + +AM_CFLAGS = \ + -DCAPWAP_MULTITHREADING_ENABLE \ + -D_REENTRANT \ + -D_GNU_SOURCE + +if DTLS_ENABLED +AM_CFLAGS += $(SSL_CFLAGS) +endif + +INCLUDES = \ + -I$(top_srcdir)/build \ + -I$(top_srcdir)/src/common \ + -I$(top_srcdir)/src/ac + +include $(top_srcdir)/build/Makefile_common.am + +ac_SOURCES = \ + $(capwap_SOURCES) \ + $(top_srcdir)/src/common/capwap_event.c \ + $(top_srcdir)/src/common/capwap_lock.c \ + $(top_srcdir)/src/ac/ac.c \ + $(top_srcdir)/src/ac/ac_execute.c \ + $(top_srcdir)/src/ac/ac_session.c \ + $(top_srcdir)/src/ac/ac_discovery.c \ + $(top_srcdir)/src/ac/ac_dfa_join.c \ + $(top_srcdir)/src/ac/ac_dfa_configure.c \ + $(top_srcdir)/src/ac/ac_dfa_imagedata.c \ + $(top_srcdir)/src/ac/ac_dfa_datacheck.c \ + $(top_srcdir)/src/ac/ac_dfa_dtls.c \ + $(top_srcdir)/src/ac/ac_dfa_run.c \ + $(top_srcdir)/src/ac/ac_dfa_reset.c \ + $(top_srcdir)/src/ac/ac_dfa_teardown.c + +ac_LDADD = \ + $(CONFIG_LIBS) \ + $(PTHREAD_LIBS) + +if DTLS_ENABLED +ac_LDADD += $(SSL_LIBS) +endif diff --git a/build/wtp/Makefile.am b/build/wtp/Makefile.am new file mode 100755 index 0000000..eb0eabb --- /dev/null +++ b/build/wtp/Makefile.am @@ -0,0 +1,60 @@ +# SmartCAPWAP -- An Open Source CAPWAP WTP / AC +# +# Copyright (C) 2012-2013 Massimo Vellucci +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program (see the file COPYING included with this +# distribution); if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +MAINTAINERCLEANFILES = \ + $(srcdir)/Makefile.in + +bin_PROGRAMS = wtp + +AM_CFLAGS = \ + -D_REENTRANT \ + -D_GNU_SOURCE + +if DTLS_ENABLED +AM_CFLAGS += $(SSL_CFLAGS) +endif + +INCLUDES = \ + -I$(top_srcdir)/build \ + -I$(top_srcdir)/src/common \ + -I$(top_srcdir)/src/wtp + +include $(top_srcdir)/build/Makefile_common.am + +wtp_SOURCES = \ + $(capwap_SOURCES) \ + $(top_srcdir)/src/wtp/wtp.c \ + $(top_srcdir)/src/wtp/wtp_dfa.c \ + $(top_srcdir)/src/wtp/wtp_dfa_idle.c \ + $(top_srcdir)/src/wtp/wtp_dfa_discovery.c \ + $(top_srcdir)/src/wtp/wtp_dfa_sulking.c \ + $(top_srcdir)/src/wtp/wtp_dfa_dtls.c \ + $(top_srcdir)/src/wtp/wtp_dfa_join.c \ + $(top_srcdir)/src/wtp/wtp_dfa_configure.c \ + $(top_srcdir)/src/wtp/wtp_dfa_datacheck.c \ + $(top_srcdir)/src/wtp/wtp_dfa_run.c \ + $(top_srcdir)/src/wtp/wtp_dfa_reset.c \ + $(top_srcdir)/src/wtp/wtp_dfa_imagedata.c + +wtp_LDADD = \ + $(CONFIG_LIBS) + +if DTLS_ENABLED +wtp_LDADD += $(SSL_LIBS) +endif diff --git a/conf/ac.conf b/conf/ac.conf new file mode 100755 index 0000000..944cac0 --- /dev/null +++ b/conf/ac.conf @@ -0,0 +1,81 @@ +# AC configuration file + +version = "1.0"; + +application: { + name = "ac 1"; + + binding = [ + "802.11" + ]; + + descriptor: { + maxstations = 20; + maxwtp = 10; + + security: { + presharedkey = false; + x509 = false; + }; + + rmacfiled: { + supported = false; + }; + + dtlspolicy: { + cleardatachannel = true; + dtlsdatachannel = true; + }; + + info = ( + { idvendor = 12345; type = "hardware"; value = "1.0"; }, + { idvendor = 33457; type = "software"; value = "2.0"; } + ); + }; + + ecn = "limited"; + + timer: { + discovery = 20; + echorequest = 30; + decrypterrorreport = 120; + idletimeout = 320; + }; + + wtpfallback = true; + + dtls: { + enable = true; + + type = "x509"; + + presharedkey: { + # TODO + }; + + x509: { + calist = "/etc/capwap/ca.crt"; + certificate = "/etc/capwap/ac.crt"; + privatekey = "/etc/capwap/ac.key"; + privatekeypassword = ""; + }; + }; + + network: { + #binding = "eth1"; + mtu = 1500; + transport = "udp"; + + ipv4 = true; + ipv6 = false; + ipdualstack = true; + }; +}; + +logging: { + enable = true; + level = "debug"; + output = ( + { mode = "stdout"; } + ); +}; diff --git a/conf/ac.crt b/conf/ac.crt new file mode 100644 index 0000000..0fdfbd2 --- /dev/null +++ b/conf/ac.crt @@ -0,0 +1,73 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 2 (0x2) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=IT, ST=Italy, L=Rome, O=Capwap CA, CN=CAPWAP_CA/emailAddress=ca@localhost + Validity + Not Before: Apr 11 17:49:25 2009 GMT + Not After : Apr 9 17:49:25 2019 GMT + Subject: C=IT, ST=Italy, L=Rome, O=Capwap AC, CN=CAPWAP_AC/emailAddress=ac@localhost + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:e2:9f:90:f6:f5:c5:52:23:cc:8d:28:6c:7c:0b: + 4a:ec:0d:50:de:0d:37:0d:de:75:cb:61:d8:58:e3: + 3f:ec:cb:5b:b5:e0:ca:87:93:cf:22:f3:7d:35:cf: + 2d:ef:ad:a4:8a:d3:8c:ec:7f:d2:7e:19:cb:11:15: + 28:b0:ee:74:33:e2:21:24:70:d0:e4:22:2f:eb:59: + 3d:ef:c4:c2:1b:f7:7d:1b:ac:3c:f8:46:c9:0a:f4: + 12:b0:71:11:0d:52:84:d9:a9:76:84:68:33:c6:7c: + 54:1a:4c:34:f4:22:ad:fb:4c:7b:2e:ee:1a:fe:b3: + 0d:18:38:16:46:48:9d:7a:b7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Cert Type: + SSL Server + Netscape Comment: + Easy-RSA Generated Server Certificate + X509v3 Subject Key Identifier: + 63:7B:01:7C:B6:2B:DC:D5:E4:BD:A1:AC:D4:BB:E0:6F:A6:07:16:A4 + X509v3 Authority Key Identifier: + keyid:49:62:46:06:E1:E5:D3:3A:08:79:C6:D6:FE:93:A8:D9:AA:75:EB:E4 + DirName:/C=IT/ST=Italy/L=Rome/O=Capwap CA/CN=CAPWAP_CA/emailAddress=ca@localhost + serial:94:59:55:20:58:F6:66:33 + + X509v3 Extended Key Usage: + TLS Web Server Authentication + X509v3 Key Usage: + Digital Signature, Key Encipherment + Signature Algorithm: sha1WithRSAEncryption + 0a:be:9e:6c:a1:c6:0c:a6:d3:5e:92:6b:09:21:78:99:f0:83: + a1:a4:2f:56:56:55:51:61:f4:04:a5:fe:9a:c4:95:76:01:f3: + 72:96:18:e2:96:f9:8d:cf:6d:32:6c:39:9c:92:a2:41:39:e2: + 3f:32:ef:73:75:f0:3e:f4:9c:93:af:31:63:c7:55:2e:8d:de: + ab:99:59:82:36:54:49:ef:ef:13:a7:26:db:8f:2e:93:0c:a1: + d5:b5:e6:5d:7a:a4:3f:1d:8e:ad:73:c5:32:ba:24:d2:4a:f5: + ae:95:18:98:07:80:fc:be:95:45:ce:50:ab:fc:6e:90:7a:8b: + 28:5d +-----BEGIN CERTIFICATE----- +MIIDmTCCAwKgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJJVDEO +MAwGA1UECBMFSXRhbHkxDTALBgNVBAcTBFJvbWUxEjAQBgNVBAoTCUNhcHdhcCBD +QTESMBAGA1UEAxQJQ0FQV0FQX0NBMRswGQYJKoZIhvcNAQkBFgxjYUBsb2NhbGhv +c3QwHhcNMDkwNDExMTc0OTI1WhcNMTkwNDA5MTc0OTI1WjBxMQswCQYDVQQGEwJJ +VDEOMAwGA1UECBMFSXRhbHkxDTALBgNVBAcTBFJvbWUxEjAQBgNVBAoTCUNhcHdh +cCBBQzESMBAGA1UEAxQJQ0FQV0FQX0FDMRswGQYJKoZIhvcNAQkBFgxhY0Bsb2Nh +bGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOKfkPb1xVIjzI0obHwL +SuwNUN4NNw3edcth2FjjP+zLW7XgyoeTzyLzfTXPLe+tpIrTjOx/0n4ZyxEVKLDu +dDPiISRw0OQiL+tZPe/Ewhv3fRusPPhGyQr0ErBxEQ1ShNmpdoRoM8Z8VBpMNPQi +rftMey7uGv6zDRg4FkZInXq3AgMBAAGjggE/MIIBOzAJBgNVHRMEAjAAMBEGCWCG +SAGG+EIBAQQEAwIGQDA0BglghkgBhvhCAQ0EJxYlRWFzeS1SU0EgR2VuZXJhdGVk +IFNlcnZlciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUY3sBfLYr3NXkvaGs1Lvgb6YH +FqQwgaMGA1UdIwSBmzCBmIAUSWJGBuHl0zoIecbW/pOo2ap16+ShdaRzMHExCzAJ +BgNVBAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsGA1UEBxMEUm9tZTESMBAGA1UE +ChMJQ2Fwd2FwIENBMRIwEAYDVQQDFAlDQVBXQVBfQ0ExGzAZBgkqhkiG9w0BCQEW +DGNhQGxvY2FsaG9zdIIJAJRZVSBY9mYzMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAsG +A1UdDwQEAwIFoDANBgkqhkiG9w0BAQUFAAOBgQAKvp5socYMptNekmsJIXiZ8IOh +pC9WVlVRYfQEpf6axJV2AfNylhjilvmNz20ybDmckqJBOeI/Mu9zdfA+9JyTrzFj +x1Uujd6rmVmCNlRJ7+8Tpybbjy6TDKHVteZdeqQ/HY6tc8UyuiTSSvWulRiYB4D8 +vpVFzlCr/G6QeosoXQ== +-----END CERTIFICATE----- diff --git a/conf/ac.key b/conf/ac.key new file mode 100644 index 0000000..247cc15 --- /dev/null +++ b/conf/ac.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQDin5D29cVSI8yNKGx8C0rsDVDeDTcN3nXLYdhY4z/sy1u14MqH +k88i8301zy3vraSK04zsf9J+GcsRFSiw7nQz4iEkcNDkIi/rWT3vxMIb930brDz4 +RskK9BKwcRENUoTZqXaEaDPGfFQaTDT0Iq37THsu7hr+sw0YOBZGSJ16twIDAQAB +AoGAM0gEV735rsKnEPIqyk8L3yqMOBMpeBv079buS/7wgjbqQ9fgPJm376LFnJ2L +OYdDip6gbwvlp99SUkVFdfQfmwjmBDk1IfEWYiPO4CUrsIUzid2KtueZW5c6Cm0Q +RMutCQrsZw9s6VpL9bmV0zsm3MYNYfERs0rPOIW9I3YyrEECQQD5t5V4Uj7+gpZO +Qvb+tYAVb8NcYTPBfdVbZJ8MRrs9tH2Y2AkuE1D+g7S5qq+Ld1tBIcCeKhlG4+ee +ClH20s3RAkEA6FM8eGKOhZ4nmu4yzwdMJnqY0d4+56805VS5Z+uJJiXvCXKQ+wQr +e/8BLsWRLxXlqijshsWzHID2g8i/rlC6BwJADiHqrqWQ9Rx03zcA/YdOGWh62PaT +VEcH1SVLrwktvZ9CYG0Rj+797XmMt9lGGBGIM5ZybUEarx1k1VfmLZ7ekQJAKPOD +FTVKKgNWt9iE3DlNEvtJNLUYIX6gtEva4paB9ld5axDmvVhe0dyBON1aWhJiCTxt +dNQkXkHdMh2QrAlOwQJAcoEhG5Zl0wATXSCFvbvaJKiH9Ab1IjlCtNI3O/+TgQjN +ZQMA0nO5ZJ3p266M8zh2hgIRdNstv4oilqh/3DnO3g== +-----END RSA PRIVATE KEY----- diff --git a/conf/ca.crt b/conf/ca.crt new file mode 100644 index 0000000..e9e27d3 --- /dev/null +++ b/conf/ca.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDNzCCAqCgAwIBAgIJAJRZVSBY9mYzMA0GCSqGSIb3DQEBBQUAMHExCzAJBgNV +BAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsGA1UEBxMEUm9tZTESMBAGA1UEChMJ +Q2Fwd2FwIENBMRIwEAYDVQQDFAlDQVBXQVBfQ0ExGzAZBgkqhkiG9w0BCQEWDGNh +QGxvY2FsaG9zdDAeFw0wOTA0MTExNzQ2MzlaFw0xOTA0MDkxNzQ2MzlaMHExCzAJ +BgNVBAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsGA1UEBxMEUm9tZTESMBAGA1UE +ChMJQ2Fwd2FwIENBMRIwEAYDVQQDFAlDQVBXQVBfQ0ExGzAZBgkqhkiG9w0BCQEW +DGNhQGxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAw0gvx6qo +B4fd23/dU5yBdj/TKzVM1/kC/bwRRFjNd2FIMpjsJ7drbwhZo5CrOSFyKn8X4Exr +EdkfMjjNFi6mZVmdFfJO4ex+6qJqc1m52AocUEOkyG/52LOnCTsT91yps97obPUF +8ezK0m9a5dnuoJ8q7lxbGu7m0PJ0Qgws3dsCAwEAAaOB1jCB0zAdBgNVHQ4EFgQU +SWJGBuHl0zoIecbW/pOo2ap16+QwgaMGA1UdIwSBmzCBmIAUSWJGBuHl0zoIecbW +/pOo2ap16+ShdaRzMHExCzAJBgNVBAYTAklUMQ4wDAYDVQQIEwVJdGFseTENMAsG +A1UEBxMEUm9tZTESMBAGA1UEChMJQ2Fwd2FwIENBMRIwEAYDVQQDFAlDQVBXQVBf +Q0ExGzAZBgkqhkiG9w0BCQEWDGNhQGxvY2FsaG9zdIIJAJRZVSBY9mYzMAwGA1Ud +EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAsHOGDPcwslke/Ux3bp2tZj4MizTL +DJqTRbqoiQLOTfUI7KU0GQ2m+ywTwhpiUPhmqqmgtGKXbL9mQB/qcG6HGNzdRc56 +4ttc1RVErhmM/LZEFjyHamuOryUY5FmS78XXBGFRkvPAap9OSQU4vXPfehYFCL+p +/L9CoKunApUqH3g= +-----END CERTIFICATE----- diff --git a/conf/wtp.conf b/conf/wtp.conf new file mode 100755 index 0000000..c7bfa8c --- /dev/null +++ b/conf/wtp.conf @@ -0,0 +1,110 @@ +# WTP configuration file + +version = "1.0"; + +application: { + name = "wtp 1"; + + location = "Ufficio"; + + binding = "802.11"; + + tunnelmode: { + nativeframe = false; + ethframe = false; + localbridging = false; + }; + + mactype = "localmac"; + + boardinfo: { + idvendor = 123456; + + element = ( + { name = "model"; value = "1.0"; }, + { name = "serial"; value = "2.0"; }, + { name = "id"; value = "3.0"; }, + { name = "revision"; value = "4.0"; }, + { name = "macaddress"; type = "interface"; value = "eth1"; } + ); + }; + + descriptor: { + radio = ( + { device = "wifi0"; type = "bg"; status = "enabled"; } + ); + + encryption = [ + "802.11_AES", + "802.11_TKIP" + ]; + + info = ( + { idvendor = 23456; type = "hardware"; value = "abcde"; }, + { idvendor = 33457; type = "software"; value = "fghil"; }, + { idvendor = 43458; type = "boot"; value = "mnopq"; }, + { idvendor = 53459; type = "other"; value = "qwert"; } + ); + }; + + ecn = "limited"; + + timer: { + statistics = 120; + }; + + dtls: { + enable = true; + + dtlspolicy: { + cleardatachannel = true; + dtlsdatachannel = true; + }; + + type = "x509"; + + presharedkey: { + # TODO + }; + + x509: { + calist = "/etc/capwap/ca.crt"; + certificate = "/etc/capwap/wtp.crt"; + privatekey = "/etc/capwap/wtp.key"; + privatekeypassword = ""; + }; + }; + + network: { + #binding = "eth1"; + mtu = 1500; + + port = 10000; + transport = "udp"; + + ipv4 = true; + ipv6 = false; + ipdualstack = true; + }; + + acdiscovery: { + search = false; + host = [ + "127.0.0.1" + ]; + }; + + acprefered: { + host = [ + "127.0.0.1" + ]; + }; +}; + +logging: { + enable = true; + level = "debug"; + output = ( + { mode = "stdout"; } + ); +}; diff --git a/conf/wtp.crt b/conf/wtp.crt new file mode 100644 index 0000000..2195670 --- /dev/null +++ b/conf/wtp.crt @@ -0,0 +1,70 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=IT, ST=Italy, L=Rome, O=Capwap CA, CN=CAPWAP_CA/emailAddress=ca@localhost + Validity + Not Before: Apr 11 17:50:00 2009 GMT + Not After : Apr 9 17:50:00 2019 GMT + Subject: C=IT, ST=Italy, L=Rome, O=Capwap WTP, CN=CAPWAP_WTP/emailAddress=wtp@localhost + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public Key: (1024 bit) + Modulus (1024 bit): + 00:d4:78:02:6f:d8:52:3b:ea:7c:13:a9:d4:74:58: + 4c:d9:6d:e9:54:76:ea:6c:74:8a:70:b7:f8:72:9e: + 71:63:bc:db:6e:43:d8:c6:6b:34:94:e4:28:98:a2: + 5b:16:9e:a8:9e:19:50:61:01:d3:f6:fd:37:e7:8b: + be:9e:bf:bc:96:c2:3e:d3:fa:fe:1a:76:42:5c:92: + 76:73:1f:97:94:42:38:93:d0:56:a5:b2:b1:5f:ba: + 90:90:17:f4:88:cb:c6:35:9b:7d:0f:a4:75:6d:d2: + bb:b2:1d:cf:55:9d:e7:9b:03:35:fe:6f:1b:df:10: + 3d:58:7b:77:1c:e5:ba:a5:75 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + Easy-RSA Generated Certificate + X509v3 Subject Key Identifier: + 53:EB:37:11:23:CC:27:53:89:04:2F:08:C5:05:47:D4:65:23:73:2D + X509v3 Authority Key Identifier: + keyid:49:62:46:06:E1:E5:D3:3A:08:79:C6:D6:FE:93:A8:D9:AA:75:EB:E4 + DirName:/C=IT/ST=Italy/L=Rome/O=Capwap CA/CN=CAPWAP_CA/emailAddress=ca@localhost + serial:94:59:55:20:58:F6:66:33 + + X509v3 Extended Key Usage: + TLS Web Client Authentication + X509v3 Key Usage: + Digital Signature + Signature Algorithm: sha1WithRSAEncryption + 57:93:d6:43:0b:e6:5f:b7:77:2c:1d:d3:b0:4c:6a:35:62:ec: + 5b:56:e5:f7:34:b9:c7:8d:9f:e7:cb:12:0d:f0:cf:c7:a3:c1: + 24:0f:64:f2:a2:ab:f7:80:e8:a5:66:8a:c3:2f:9b:4f:87:d7: + 8e:27:e7:29:73:cc:fc:4c:0d:fb:8f:74:ee:7c:bd:ce:d3:01: + ee:69:1f:42:56:6f:e1:b3:6c:c3:a1:4d:60:14:06:ba:a7:38: + a0:78:1d:a9:cd:39:90:f2:33:6c:fb:48:cb:6e:80:00:ce:7c: + 50:e2:e7:6f:83:f1:86:be:39:5c:0c:64:1f:01:7c:1b:9f:e6: + ea:e2 +-----BEGIN CERTIFICATE----- +MIIDgjCCAuugAwIBAgIBAzANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJJVDEO +MAwGA1UECBMFSXRhbHkxDTALBgNVBAcTBFJvbWUxEjAQBgNVBAoTCUNhcHdhcCBD +QTESMBAGA1UEAxQJQ0FQV0FQX0NBMRswGQYJKoZIhvcNAQkBFgxjYUBsb2NhbGhv +c3QwHhcNMDkwNDExMTc1MDAwWhcNMTkwNDA5MTc1MDAwWjB0MQswCQYDVQQGEwJJ +VDEOMAwGA1UECBMFSXRhbHkxDTALBgNVBAcTBFJvbWUxEzARBgNVBAoTCkNhcHdh +cCBXVFAxEzARBgNVBAMUCkNBUFdBUF9XVFAxHDAaBgkqhkiG9w0BCQEWDXd0cEBs +b2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANR4Am/YUjvqfBOp +1HRYTNlt6VR26mx0inC3+HKecWO8225D2MZrNJTkKJiiWxaeqJ4ZUGEB0/b9N+eL +vp6/vJbCPtP6/hp2QlySdnMfl5RCOJPQVqWysV+6kJAX9IjLxjWbfQ+kdW3Su7Id +z1Wd55sDNf5vG98QPVh7dxzluqV1AgMBAAGjggElMIIBITAJBgNVHRMEAjAAMC0G +CWCGSAGG+EIBDQQgFh5FYXN5LVJTQSBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYD +VR0OBBYEFFPrNxEjzCdTiQQvCMUFR9RlI3MtMIGjBgNVHSMEgZswgZiAFEliRgbh +5dM6CHnG1v6TqNmqdevkoXWkczBxMQswCQYDVQQGEwJJVDEOMAwGA1UECBMFSXRh +bHkxDTALBgNVBAcTBFJvbWUxEjAQBgNVBAoTCUNhcHdhcCBDQTESMBAGA1UEAxQJ +Q0FQV0FQX0NBMRswGQYJKoZIhvcNAQkBFgxjYUBsb2NhbGhvc3SCCQCUWVUgWPZm +MzATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQEF +BQADgYEAV5PWQwvmX7d3LB3TsExqNWLsW1bl9zS5x42f58sSDfDPx6PBJA9k8qKr +94DopWaKwy+bT4fXjifnKXPM/EwN+4907ny9ztMB7mkfQlZv4bNsw6FNYBQGuqc4 +oHgdqc05kPIzbPtIy26AAM58UOLnb4Pxhr45XAxkHwF8G5/m6uI= +-----END CERTIFICATE----- diff --git a/conf/wtp.key b/conf/wtp.key new file mode 100644 index 0000000..f81f7a9 --- /dev/null +++ b/conf/wtp.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQDUeAJv2FI76nwTqdR0WEzZbelUdupsdIpwt/hynnFjvNtuQ9jG +azSU5CiYolsWnqieGVBhAdP2/Tfni76ev7yWwj7T+v4adkJcknZzH5eUQjiT0Fal +srFfupCQF/SIy8Y1m30PpHVt0ruyHc9VneebAzX+bxvfED1Ye3cc5bqldQIDAQAB +AoGAAIP2BY1xzaFpAac0079vGEvy9/h94xt5RoK7wJNv1P0dTyws93ZFH2NaUdx6 ++hgScqe/ES0u9XdxzkcBhGMUswvNmofgk9w/ynQhey7g739TXeEh76zMphNVJf0z +pawFFP/FIIgYVf7CdHkQ8cU4LEvTUCxAE5bICHMuOgCiEQECQQDz5/PSc/ZuMOZq +ngYOCaf3p3CQdeFcFOeU3ldxW8j5eZR2Xdhu5CCeY5ELPq+yW4+J+6KBAVh6Q8Uv +MwDCSqrBAkEA3wEBMgaJvpEijv6P6Ryc+khazEASDAbvUmrcUD0t14aPrCoAHAUU +stCCcn8zV4MqETPr7bO9joh/oh7IpCGrtQJAMYzTydNpIuWoScykqkFn8DYB9jcc +e1p72ZB57zuYTqZWdN2H1K9fuQ29L62if/d956hJUS/2y4/tBTO1WcNVgQJBALQK +jJuF2cmoRV1rOma6+iW7KstCYw+gvbEtoyeI7SzXb8FQu3vjwj1HyDmGZr4doGX5 +JF5pwf2ESH9sGRMiOA0CQGKVeegxodjqwNEY8WqJjgROHkI1aJhdHVehAcCLhtGX +WBcQEK73GJC5Kb4yTg0VEFkIWeGpy0aDnOyTJlNrQKc= +-----END RSA PRIVATE KEY----- diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..be01770 --- /dev/null +++ b/configure.ac @@ -0,0 +1,230 @@ +# SmartCAPWAP -- An Open Source CAPWAP WTP / AC +# +# Copyright (C) 2012-2013 Massimo Vellucci +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program (see the file COPYING included with this +# distribution); if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +AC_PREREQ(2.60) + +m4_include(version.m4) +AC_INIT([PRODUCT_NAME], [PRODUCT_VERSION], [PRODUCT_BUGREPORT], [PRODUCT_TARNAME]) +AC_CONFIG_AUX_DIR([.]) +AC_CONFIG_MACRO_DIR([m4]) +AM_INIT_AUTOMAKE + +# cross-compile macros +AC_CANONICAL_BUILD +AC_CANONICAL_HOST + +# +AC_COPYRIGHT("SmartCapwap by Massimo Vellucci ") +AC_REVISION($Revision: 1.0 $) + +# +AC_ARG_ENABLE( + [dtls], + [AS_HELP_STRING([--disable-dtls], [disable DTLS support @<:@default=yes@:>@])], + , + [enable_dtls="yes"] +) + +AC_ARG_ENABLE( + [debug], + [AS_HELP_STRING([--disable-debug], [disable debug support @<:@default=yes@:>@])], + , + [enable_debug="yes"] +) + +AC_ARG_ENABLE( + [logging], + [AS_HELP_STRING([--disable-logging], [disable logging support @<:@default=yes@:>@])], + , + [enable_logging="yes"] +) + +AC_ARG_ENABLE( + [ac], + [AS_HELP_STRING([--disable-ac], [disable ac support @<:@default=yes@:>@])], + , + [enable_ac="yes"] +) + +AC_ARG_ENABLE( + [wtp], + [AS_HELP_STRING([--disable-wtp], [disable wtp support @<:@default=yes@:>@])], + , + [enable_wtp="yes"] +) + +AC_ARG_WITH( + [ssl-library], + [AS_HELP_STRING([--with-crypto-library=library], [build with the given crypto library, TYPE=openssl @<:@default=openssl@:>@])], + [ + case "${withval}" in + openssl) ;; + *) AC_MSG_ERROR([bad value ${withval} for --with-crypto-library]) ;; + esac + ], + [with_ssl_library="openssl"] +) + +AC_ARG_WITH( + [mem-check], + [AS_HELP_STRING([--with-mem-check=TYPE], [build with debug memory checking, TYPE=no|internal|valgrind @<:@default=internal@:>@])], + [ + case "${withval}" in + valgrind|internal|no) ;; + *) AC_MSG_ERROR([bad value ${withval} for --mem-check]) ;; + esac + ], + [with_mem_check="internal"] +) + +# specify output header file +AM_CONFIG_HEADER(build/config.h) + +# +old_CFLAGS="${CFLAGS}" +AC_PROG_CC([gcc]) +CFLAGS="${old_CFLAGS}" + +# +AM_CONDITIONAL([DEBUG_BUILD], [test "$enable_debug" = yes]) +if test "${enable_debug}" = "yes"; then + CFLAGS="${CFLAGS} -DDEBUG -Wall -Werror -g -O0" +else + CFLAGS="${CFLAGS} -O2" +fi + +# +AC_PROG_INSTALL +AC_USE_SYSTEM_EXTENSIONS + +AC_LANG(C) + +AC_HEADER_STDC + +# Check LIBCONFIG library +AC_CHECK_HEADER([libconfig.h], [], [AC_MSG_ERROR(You need the libconfig headers)]) +AC_CHECK_LIB([config], [config_init], [CONFIG_LIBS="-lconfig"], [AC_MSG_ERROR(You need the libconfig library)]) + +# Check PTHREAD library +AC_CHECK_HEADER([pthread.h], [], [AC_MSG_ERROR(You need the pthread headers)]) +AC_CHECK_LIB([pthread], [pthread_create], [PTHREAD_LIBS="-lpthread"], [AC_MSG_ERROR(You need the pthread library)]) + +# Check SSL library +PKG_CHECK_MODULES( + [OPENSSL_CRYPTO], + [libcrypto >= 1.0.0], + [have_openssl_crypto="yes"], + [AC_CHECK_LIB( + [crypto], + [RSA_new], + [ + have_openssl_crypto="yes" + OPENSSL_CRYPTO_LIBS="-lcrypto" + ] + )] +) + +PKG_CHECK_MODULES( + [OPENSSL_SSL], + [libssl >= 1.0.0], + [have_openssl_ssl="yes"], + [AC_CHECK_LIB( + [ssl], + [SSL_CTX_new], + [ + have_openssl_ssl="yes" + OPENSSL_SSL_LIBS="-lssl" + ] + )] +) + +if test "${have_openssl_ssl}" = "yes"; then + saved_CFLAGS="${CFLAGS}" + saved_LIBS="${LIBS}" + CFLAGS="${CFLAGS} ${OPENSSL_SSL_CFLAGS}" + LIBS="${LIBS} ${OPENSSL_SSL_LIBS}" + have_openssl_engine="yes" + AC_CHECK_FUNC([SSL_CTX_set_cookie_generate_cb], , [AC_MSG_ERROR([${with_ssl_library} SSL_CTX_set_cookie_generate_cb function is required but missing])]) + AC_CHECK_FUNC([SSL_CTX_set_cookie_verify_cb], , [AC_MSG_ERROR([${with_ssl_library} SSL_CTX_set_cookie_verify_cb function is required but missing])]) + CFLAGS="${saved_CFLAGS}" + LIBS="${saved_LIBS}" +fi + +case "${with_ssl_library}" in + openssl) + have_crypto_engine="${have_openssl_engine}" + have_crypto_crypto="${have_openssl_crypto}" + have_crypto_ssl="${have_openssl_ssl}" + SSL_CFLAGS="${OPENSSL_CRYPTO_CFLAGS} ${OPENSSL_SSL_CFLAGS}" + SSL_LIBS="${OPENSSL_SSL_LIBS}" + test "${have_openssl_engine}" = "yes" && AC_DEFINE([HAVE_OPENSSL_ENGINE], [1], [Use crypto library]) + ;; +esac + +if test "${enable_dtls}" = "yes"; then + test "${have_crypto_engine}" != "yes" && AC_MSG_ERROR([${with_ssl_library} engine is required but missing]) + test "${have_crypto_crypto}" != "yes" && AC_MSG_ERROR([${with_ssl_library} crypto is required but missing]) + test "${have_crypto_ssl}" != "yes" && AC_MSG_ERROR([${with_ssl_library} ssl is required but missing]) + AC_DEFINE([ENABLE_DTLS], [1], [Enable DTLS]) +fi + +# Memory check +case "${with_mem_check}" in + internal) + LIBS="${LIBS} -rdynamic" + AC_DEFINE([USE_INTERNAL_MEMCHECK], [1], [Use internal memory debugging]) + ;; + valgrind) + AC_CHECK_HEADER( + [valgrind/memcheck.h], + [ + CFLAGS="${CFLAGS} -g -fno-inline" + AC_DEFINE([USE_VALGRIND_MEMCHECK], [1], [Use valgrind memory debugging library]) + ], + [AC_MSG_ERROR([valgrind headers not found.])] + ) + ;; +esac + +# +AM_CONDITIONAL([BUILD_AC], [test "${enable_ac}" = "yes"]) +AM_CONDITIONAL([BUILD_WTP], [test "${enable_wtp}" = "yes"]) + +# +test "${enable_logging}" = "yes" && AC_DEFINE([ENABLE_LOGGING], [1], [Enable logging]) + +# +AM_CONDITIONAL([BUILD_DEBUG], [test "${enable_debug}" = "yes"]) +AM_CONDITIONAL([DTLS_ENABLED], [test "${enable_dtls}" = "yes"]) + +# +AC_SUBST([SSL_CFLAGS]) +AC_SUBST([SSL_LIBS]) +AC_SUBST([CONFIG_LIBS]) +AC_SUBST([PTHREAD_LIBS]) + +# +AC_CONFIG_FILES([ + Makefile + build/Makefile + build/ac/Makefile + build/wtp/Makefile +]) + +AC_OUTPUT diff --git a/docs/rfc5415.txt b/docs/rfc5415.txt new file mode 100644 index 0000000..fcb71d8 --- /dev/null +++ b/docs/rfc5415.txt @@ -0,0 +1,8683 @@ + + + + + + +Network Working Group P. Calhoun, Ed. +Request for Comments: 5415 Cisco Systems, Inc. +Category: Standards Track M. Montemurro, Ed. + Research In Motion + D. Stanley, Ed. + Aruba Networks + March 2009 + + + Control And Provisioning of Wireless Access Points (CAPWAP) + Protocol Specification + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (c) 2009 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents in effect on the date of + publication of this document (http://trustee.ietf.org/license-info). + Please review these documents carefully, as they describe your rights + and restrictions with respect to this document. + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + + + + + + + + + +Calhoun, et al. Standards Track [Page 1] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +Abstract + + This specification defines the Control And Provisioning of Wireless + Access Points (CAPWAP) Protocol, meeting the objectives defined by + the CAPWAP Working Group in RFC 4564. The CAPWAP protocol is + designed to be flexible, allowing it to be used for a variety of + wireless technologies. This document describes the base CAPWAP + protocol, while separate binding extensions will enable its use with + additional wireless technologies. + +Table of Contents + + 1. Introduction ....................................................7 + 1.1. Goals ......................................................8 + 1.2. Conventions Used in This Document ..........................9 + 1.3. Contributing Authors .......................................9 + 1.4. Terminology ...............................................10 + 2. Protocol Overview ..............................................11 + 2.1. Wireless Binding Definition ...............................12 + 2.2. CAPWAP Session Establishment Overview .....................13 + 2.3. CAPWAP State Machine Definition ...........................15 + 2.3.1. CAPWAP Protocol State Transitions ..................17 + 2.3.2. CAPWAP/DTLS Interface ..............................31 + 2.4. Use of DTLS in the CAPWAP Protocol ........................33 + 2.4.1. DTLS Handshake Processing ..........................33 + 2.4.2. DTLS Session Establishment .........................35 + 2.4.3. DTLS Error Handling ................................35 + 2.4.4. DTLS Endpoint Authentication and Authorization .....36 + 3. CAPWAP Transport ...............................................40 + 3.1. UDP Transport .............................................40 + 3.2. UDP-Lite Transport ........................................41 + 3.3. AC Discovery ..............................................41 + 3.4. Fragmentation/Reassembly ..................................42 + 3.5. MTU Discovery .............................................43 + 4. CAPWAP Packet Formats ..........................................43 + 4.1. CAPWAP Preamble ...........................................46 + 4.2. CAPWAP DTLS Header ........................................46 + 4.3. CAPWAP Header .............................................47 + 4.4. CAPWAP Data Messages ......................................50 + 4.4.1. CAPWAP Data Channel Keep-Alive .....................51 + 4.4.2. Data Payload .......................................52 + 4.4.3. Establishment of a DTLS Data Channel ...............52 + 4.5. CAPWAP Control Messages ...................................52 + 4.5.1. Control Message Format .............................53 + 4.5.2. Quality of Service .................................56 + 4.5.3. Retransmissions ....................................57 + 4.6. CAPWAP Protocol Message Elements ..........................58 + 4.6.1. AC Descriptor ......................................61 + + + +Calhoun, et al. Standards Track [Page 2] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 4.6.2. AC IPv4 List .......................................64 + 4.6.3. AC IPv6 List .......................................64 + 4.6.4. AC Name ............................................65 + 4.6.5. AC Name with Priority ..............................65 + 4.6.6. AC Timestamp .......................................66 + 4.6.7. Add MAC ACL Entry ..................................66 + 4.6.8. Add Station ........................................67 + 4.6.9. CAPWAP Control IPv4 Address ........................68 + 4.6.10. CAPWAP Control IPv6 Address .......................68 + 4.6.11. CAPWAP Local IPv4 Address .........................69 + 4.6.12. CAPWAP Local IPv6 Address .........................69 + 4.6.13. CAPWAP Timers .....................................70 + 4.6.14. CAPWAP Transport Protocol .........................71 + 4.6.15. Data Transfer Data ................................72 + 4.6.16. Data Transfer Mode ................................73 + 4.6.17. Decryption Error Report ...........................73 + 4.6.18. Decryption Error Report Period ....................74 + 4.6.19. Delete MAC ACL Entry ..............................74 + 4.6.20. Delete Station ....................................75 + 4.6.21. Discovery Type ....................................75 + 4.6.22. Duplicate IPv4 Address ............................76 + 4.6.23. Duplicate IPv6 Address ............................77 + 4.6.24. Idle Timeout ......................................78 + 4.6.25. ECN Support .......................................78 + 4.6.26. Image Data ........................................79 + 4.6.27. Image Identifier ..................................79 + 4.6.28. Image Information .................................80 + 4.6.29. Initiate Download .................................81 + 4.6.30. Location Data .....................................81 + 4.6.31. Maximum Message Length ............................81 + 4.6.32. MTU Discovery Padding .............................82 + 4.6.33. Radio Administrative State ........................82 + 4.6.34. Radio Operational State ...........................83 + 4.6.35. Result Code .......................................84 + 4.6.36. Returned Message Element ..........................85 + 4.6.37. Session ID ........................................86 + 4.6.38. Statistics Timer ..................................87 + 4.6.39. Vendor Specific Payload ...........................87 + 4.6.40. WTP Board Data ....................................88 + 4.6.41. WTP Descriptor ....................................89 + 4.6.42. WTP Fallback ......................................92 + 4.6.43. WTP Frame Tunnel Mode .............................92 + 4.6.44. WTP MAC Type ......................................93 + 4.6.45. WTP Name ..........................................94 + 4.6.46. WTP Radio Statistics ..............................94 + 4.6.47. WTP Reboot Statistics .............................96 + 4.6.48. WTP Static IP Address Information .................97 + 4.7. CAPWAP Protocol Timers ....................................98 + + + +Calhoun, et al. Standards Track [Page 3] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 4.7.1. ChangeStatePendingTimer ............................98 + 4.7.2. DataChannelKeepAlive ...............................98 + 4.7.3. DataChannelDeadInterval ............................99 + 4.7.4. DataCheckTimer .....................................99 + 4.7.5. DiscoveryInterval ..................................99 + 4.7.6. DTLSSessionDelete ..................................99 + 4.7.7. EchoInterval .......................................99 + 4.7.8. IdleTimeout ........................................99 + 4.7.9. ImageDataStartTimer ...............................100 + 4.7.10. MaxDiscoveryInterval .............................100 + 4.7.11. ReportInterval ...................................100 + 4.7.12. RetransmitInterval ...............................100 + 4.7.13. SilentInterval ...................................100 + 4.7.14. StatisticsTimer ..................................100 + 4.7.15. WaitDTLS .........................................101 + 4.7.16. WaitJoin .........................................101 + 4.8. CAPWAP Protocol Variables ................................101 + 4.8.1. AdminState ........................................101 + 4.8.2. DiscoveryCount ....................................101 + 4.8.3. FailedDTLSAuthFailCount ...........................101 + 4.8.4. FailedDTLSSessionCount ............................101 + 4.8.5. MaxDiscoveries ....................................102 + 4.8.6. MaxFailedDTLSSessionRetry .........................102 + 4.8.7. MaxRetransmit .....................................102 + 4.8.8. RetransmitCount ...................................102 + 4.8.9. WTPFallBack .......................................102 + 4.9. WTP Saved Variables ......................................102 + 4.9.1. AdminRebootCount ..................................102 + 4.9.2. FrameEncapType ....................................102 + 4.9.3. LastRebootReason ..................................103 + 4.9.4. MacType ...........................................103 + 4.9.5. PreferredACs ......................................103 + 4.9.6. RebootCount .......................................103 + 4.9.7. Static IP Address .................................103 + 4.9.8. WTPLinkFailureCount ...............................103 + 4.9.9. WTPLocation .......................................103 + 4.9.10. WTPName ..........................................103 + 5. CAPWAP Discovery Operations ...................................103 + 5.1. Discovery Request Message ................................103 + 5.2. Discovery Response Message ...............................105 + 5.3. Primary Discovery Request Message ........................106 + 5.4. Primary Discovery Response ...............................107 + 6. CAPWAP Join Operations ........................................108 + 6.1. Join Request .............................................108 + 6.2. Join Response ............................................110 + 7. Control Channel Management ....................................111 + 7.1. Echo Request .............................................111 + 7.2. Echo Response ............................................112 + + + +Calhoun, et al. Standards Track [Page 4] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 8. WTP Configuration Management ..................................112 + 8.1. Configuration Consistency ................................112 + 8.1.1. Configuration Flexibility .........................113 + 8.2. Configuration Status Request .............................114 + 8.3. Configuration Status Response ............................115 + 8.4. Configuration Update Request .............................116 + 8.5. Configuration Update Response ............................117 + 8.6. Change State Event Request ...............................117 + 8.7. Change State Event Response ..............................118 + 8.8. Clear Configuration Request ..............................119 + 8.9. Clear Configuration Response .............................119 + 9. Device Management Operations ..................................120 + 9.1. Firmware Management ......................................120 + 9.1.1. Image Data Request ................................124 + 9.1.2. Image Data Response ...............................125 + 9.2. Reset Request ............................................126 + 9.3. Reset Response ...........................................127 + 9.4. WTP Event Request ........................................127 + 9.5. WTP Event Response .......................................128 + 9.6. Data Transfer ............................................128 + 9.6.1. Data Transfer Request .............................130 + 9.6.2. Data Transfer Response ............................131 + 10. Station Session Management ...................................131 + 10.1. Station Configuration Request ...........................131 + 10.2. Station Configuration Response ..........................132 + 11. NAT Considerations ...........................................132 + 12. Security Considerations ......................................134 + 12.1. CAPWAP Security .........................................134 + 12.1.1. Converting Protected Data into Unprotected Data ..135 + 12.1.2. Converting Unprotected Data into + Protected Data (Insertion) .......................135 + 12.1.3. Deletion of Protected Records ....................135 + 12.1.4. Insertion of Unprotected Records .................135 + 12.1.5. Use of MD5 .......................................136 + 12.1.6. CAPWAP Fragmentation .............................136 + 12.2. Session ID Security .....................................136 + 12.3. Discovery or DTLS Setup Attacks .........................137 + 12.4. Interference with a DTLS Session ........................137 + 12.5. CAPWAP Pre-Provisioning .................................138 + 12.6. Use of Pre-Shared Keys in CAPWAP ........................139 + 12.7. Use of Certificates in CAPWAP ...........................140 + 12.8. Use of MAC Address in CN Field ..........................140 + 12.9. AAA Security ............................................141 + 12.10. WTP Firmware ...........................................141 + 13. Operational Considerations ...................................141 + 14. Transport Considerations .....................................142 + 15. IANA Considerations ..........................................143 + 15.1. IPv4 Multicast Address ..................................143 + + + +Calhoun, et al. Standards Track [Page 5] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 15.2. IPv6 Multicast Address ..................................144 + 15.3. UDP Port ................................................144 + 15.4. CAPWAP Message Types ....................................144 + 15.5. CAPWAP Header Flags .....................................144 + 15.6. CAPWAP Control Message Flags ............................145 + 15.7. CAPWAP Message Element Type .............................145 + 15.8. CAPWAP Wireless Binding Identifiers .....................145 + 15.9. AC Security Types .......................................146 + 15.10. AC DTLS Policy .........................................146 + 15.11. AC Information Type ....................................146 + 15.12. CAPWAP Transport Protocol Types ........................146 + 15.13. Data Transfer Type .....................................147 + 15.14. Data Transfer Mode .....................................147 + 15.15. Discovery Types ........................................147 + 15.16. ECN Support ............................................148 + 15.17. Radio Admin State ......................................148 + 15.18. Radio Operational State ................................148 + 15.19. Radio Failure Causes ...................................148 + 15.20. Result Code ............................................149 + 15.21. Returned Message Element Reason ........................149 + 15.22. WTP Board Data Type ....................................149 + 15.23. WTP Descriptor Type ....................................149 + 15.24. WTP Fallback Mode ......................................150 + 15.25. WTP Frame Tunnel Mode ..................................150 + 15.26. WTP MAC Type ...........................................150 + 15.27. WTP Radio Stats Failure Type ...........................151 + 15.28. WTP Reboot Stats Failure Type ..........................151 + 16. Acknowledgments ..............................................151 + 17. References ...................................................151 + 17.1. Normative References ....................................151 + 17.2. Informative References ..................................153 + + + + + + + + + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 6] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +1. Introduction + + This document describes the CAPWAP protocol, a standard, + interoperable protocol that enables an Access Controller (AC) to + manage a collection of Wireless Termination Points (WTPs). The + CAPWAP protocol is defined to be independent of Layer 2 (L2) + technology, and meets the objectives in "Objectives for Control and + Provisioning of Wireless Access Points (CAPWAP)" [RFC4564]. + + The emergence of centralized IEEE 802.11 Wireless Local Area Network + (WLAN) architectures, in which simple IEEE 802.11 WTPs are managed by + an Access Controller (AC), suggested that a standards-based, + interoperable protocol could radically simplify the deployment and + management of wireless networks. WTPs require a set of dynamic + management and control functions related to their primary task of + connecting the wireless and wired mediums. Traditional protocols for + managing WTPs are either manual static configuration via HTTP, + proprietary Layer 2-specific or non-existent (if the WTPs are self- + contained). An IEEE 802.11 binding is defined in [RFC5416] to + support use of the CAPWAP protocol with IEEE 802.11 WLAN networks. + + CAPWAP assumes a network configuration consisting of multiple WTPs + communicating via the Internet Protocol (IP) to an AC. WTPs are + viewed as remote radio frequency (RF) interfaces controlled by the + AC. The CAPWAP protocol supports two modes of operation: Split and + Local MAC (medium access control). In Split MAC mode, all L2 + wireless data and management frames are encapsulated via the CAPWAP + protocol and exchanged between the AC and the WTP. As shown in + Figure 1, the wireless frames received from a mobile device, which is + referred to in this specification as a Station (STA), are directly + encapsulated by the WTP and forwarded to the AC. + + +-+ wireless frames +-+ + | |--------------------------------| | + | | +-+ | | + | |--------------| |---------------| | + | |wireless PHY/ | | CAPWAP | | + | | MAC sublayer | | | | + +-+ +-+ +-+ + STA WTP AC + + Figure 1: Representative CAPWAP Architecture for Split MAC + + The Local MAC mode of operation allows for the data frames to be + either locally bridged or tunneled as 802.3 frames. The latter + implies that the WTP performs the 802.11 Integration function. In + either case, the L2 wireless management frames are processed locally + + + + +Calhoun, et al. Standards Track [Page 7] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + by the WTP and then forwarded to the AC. Figure 2 shows the Local + MAC mode, in which a station transmits a wireless frame that is + encapsulated in an 802.3 frame and forwarded to the AC. + + +-+wireless frames +-+ 802.3 frames +-+ + | |----------------| |--------------| | + | | | | | | + | |----------------| |--------------| | + | |wireless PHY/ | | CAPWAP | | + | | MAC sublayer | | | | + +-+ +-+ +-+ + STA WTP AC + + Figure 2: Representative CAPWAP Architecture for Local MAC + + Provisioning WTPs with security credentials and managing which WTPs + are authorized to provide service are traditionally handled by + proprietary solutions. Allowing these functions to be performed from + a centralized AC in an interoperable fashion increases manageability + and allows network operators to more tightly control their wireless + network infrastructure. + +1.1. Goals + + The goals for the CAPWAP protocol are listed below: + + 1. To centralize the authentication and policy enforcement functions + for a wireless network. The AC may also provide centralized + bridging, forwarding, and encryption of user traffic. + Centralization of these functions will enable reduced cost and + higher efficiency by applying the capabilities of network + processing silicon to the wireless network, as in wired LANs. + + 2. To enable shifting of the higher-level protocol processing from + the WTP. This leaves the time-critical applications of wireless + control and access in the WTP, making efficient use of the + computing power available in WTPs, which are subject to severe + cost pressure. + + 3. To provide an extensible protocol that is not bound to a specific + wireless technology. Extensibility is provided via a generic + encapsulation and transport mechanism, enabling the CAPWAP + protocol to be applied to many access point types in the future, + via a specific wireless binding. + + The CAPWAP protocol concerns itself solely with the interface between + the WTP and the AC. Inter-AC and station-to-AC communication are + strictly outside the scope of this document. + + + +Calhoun, et al. Standards Track [Page 8] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +1.2. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in RFC 2119 [RFC2119]. + +1.3. Contributing Authors + + This section lists and acknowledges the authors of significant text + and concepts included in this specification. + + The CAPWAP Working Group selected the Lightweight Access Point + Protocol (LWAPP) [LWAPP] to be used as the basis of the CAPWAP + protocol specification. The following people are authors of the + LWAPP document: + + Bob O'Hara + Email: bob.ohara@computer.org + + Pat Calhoun, Cisco Systems, Inc. + 170 West Tasman Drive, San Jose, CA 95134 + Phone: +1 408-902-3240, Email: pcalhoun@cisco.com + + Rohit Suri, Cisco Systems, Inc. + 170 West Tasman Drive, San Jose, CA 95134 + Phone: +1 408-853-5548, Email: rsuri@cisco.com + + Nancy Cam Winget, Cisco Systems, Inc. + 170 West Tasman Drive, San Jose, CA 95134 + Phone: +1 408-853-0532, Email: ncamwing@cisco.com + + Scott Kelly, Aruba Networks + 1322 Crossman Ave, Sunnyvale, CA 94089 + Phone: +1 408-754-8408, Email: skelly@arubanetworks.com + + Michael Glenn Williams, Nokia, Inc. + 313 Fairchild Drive, Mountain View, CA 94043 + Phone: +1 650-714-7758, Email: Michael.G.Williams@Nokia.com + + Sue Hares, Green Hills Software + 825 Victors Way, Suite 100, Ann Arbor, MI 48108 + Phone: +1 734 222 1610, Email: shares@ndzh.com + + Datagram Transport Layer Security (DTLS) [RFC4347] is used as the + security solution for the CAPWAP protocol. The following people are + authors of significant DTLS-related text included in this document: + + + + + +Calhoun, et al. Standards Track [Page 9] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Scott Kelly, Aruba Networks + 1322 Crossman Ave, Sunnyvale, CA 94089 + Phone: +1 408-754-8408 + Email: skelly@arubanetworks.com + + Eric Rescorla, Network Resonance + 2483 El Camino Real, #212,Palo Alto CA, 94303 + Email: ekr@networkresonance.com + + The concept of using DTLS to secure the CAPWAP protocol was part of + the Secure Light Access Point Protocol (SLAPP) proposal [SLAPP]. The + following people are authors of the SLAPP proposal: + + Partha Narasimhan, Aruba Networks + 1322 Crossman Ave, Sunnyvale, CA 94089 + Phone: +1 408-480-4716 + Email: partha@arubanetworks.com + + Dan Harkins + Trapeze Networks + 5753 W. Las Positas Blvd, Pleasanton, CA 94588 + Phone: +1-925-474-2212 + EMail: dharkins@trpz.com + + Subbu Ponnuswamy, Aruba Networks + 1322 Crossman Ave, Sunnyvale, CA 94089 + Phone: +1 408-754-1213 + Email: subbu@arubanetworks.com + + The following individuals contributed significant security-related + text to the document [RFC5418]: + + T. Charles Clancy, Laboratory for Telecommunications Sciences, + 8080 Greenmead Drive, College Park, MD 20740 + Phone: +1 240-373-5069, Email: clancy@ltsnet.net + + Scott Kelly, Aruba Networks + 1322 Crossman Ave, Sunnyvale, CA 94089 + Phone: +1 408-754-8408, Email: scott@hyperthought.com + +1.4. Terminology + + Access Controller (AC): The network entity that provides WTP access + to the network infrastructure in the data plane, control plane, + management plane, or a combination therein. + + + + + + +Calhoun, et al. Standards Track [Page 10] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + CAPWAP Control Channel: A bi-directional flow defined by the AC IP + Address, WTP IP Address, AC control port, WTP control port, and the + transport-layer protocol (UDP or UDP-Lite) over which CAPWAP Control + packets are sent and received. + + CAPWAP Data Channel: A bi-directional flow defined by the AC IP + Address, WTP IP Address, AC data port, WTP data port, and the + transport-layer protocol (UDP or UDP-Lite) over which CAPWAP Data + packets are sent and received. + + Station (STA): A device that contains an interface to a wireless + medium (WM). + + Wireless Termination Point (WTP): The physical or network entity that + contains an RF antenna and wireless Physical Layer (PHY) to transmit + and receive station traffic for wireless access networks. + + This document uses additional terminology defined in [RFC3753]. + +2. Protocol Overview + + The CAPWAP protocol is a generic protocol defining AC and WTP control + and data plane communication via a CAPWAP protocol transport + mechanism. CAPWAP Control messages, and optionally CAPWAP Data + messages, are secured using Datagram Transport Layer Security (DTLS) + [RFC4347]. DTLS is a standards-track IETF protocol based upon TLS. + The underlying security-related protocol mechanisms of TLS have been + successfully deployed for many years. + + The CAPWAP protocol transport layer carries two types of payload, + CAPWAP Data messages and CAPWAP Control messages. CAPWAP Data + messages encapsulate forwarded wireless frames. CAPWAP protocol + Control messages are management messages exchanged between a WTP and + an AC. The CAPWAP Data and Control packets are sent over separate + UDP ports. Since both data and control packets can exceed the + Maximum Transmission Unit (MTU) length, the payload of a CAPWAP Data + or Control message can be fragmented. The fragmentation behavior is + defined in Section 3. + + The CAPWAP Protocol begins with a Discovery phase. The WTPs send a + Discovery Request message, causing any Access Controller (AC) + receiving the message to respond with a Discovery Response message. + From the Discovery Response messages received, a WTP selects an AC + with which to establish a secure DTLS session. In order to establish + the secure DTLS connection, the WTP will need some amount of pre- + provisioning, which is specified in Section 12.5. CAPWAP protocol + messages will be fragmented to the maximum length discovered to be + supported by the network. + + + +Calhoun, et al. Standards Track [Page 11] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Once the WTP and the AC have completed DTLS session establishment, a + configuration exchange occurs in which both devices agree on version + information. During this exchange, the WTP may receive provisioning + settings. The WTP is then enabled for operation. + + When the WTP and AC have completed the version and provision exchange + and the WTP is enabled, the CAPWAP protocol is used to encapsulate + the wireless data frames sent between the WTP and AC. The CAPWAP + protocol will fragment the L2 frames if the size of the encapsulated + wireless user data (Data) or protocol control (Management) frames + causes the resulting CAPWAP protocol packet to exceed the MTU + supported between the WTP and AC. Fragmented CAPWAP packets are + reassembled to reconstitute the original encapsulated payload. MTU + Discovery and Fragmentation are described in Section 3. + + The CAPWAP protocol provides for the delivery of commands from the AC + to the WTP for the management of stations that are communicating with + the WTP. This may include the creation of local data structures in + the WTP for the stations and the collection of statistical + information about the communication between the WTP and the stations. + The CAPWAP protocol provides a mechanism for the AC to obtain + statistical information collected by the WTP. + + The CAPWAP protocol provides for a keep-alive feature that preserves + the communication channel between the WTP and AC. If the AC fails to + appear alive, the WTP will try to discover a new AC. + +2.1. Wireless Binding Definition + + The CAPWAP protocol is independent of a specific WTP radio + technology, as well its associated wireless link layer protocol. + Elements of the CAPWAP protocol are designed to accommodate the + specific needs of each wireless technology in a standard way. + Implementation of the CAPWAP protocol for a particular wireless + technology MUST follow the binding requirements defined for that + technology. + + When defining a binding for wireless technologies, the authors MUST + include any necessary definitions for technology-specific messages + and all technology-specific message elements for those messages. At + a minimum, a binding MUST provide: + + 1. The definition for a binding-specific Statistics message element, + carried in the WTP Event Request message. + + 2. A message element carried in the Station Configuration Request + message to configure station information on the WTP. + + + + +Calhoun, et al. Standards Track [Page 12] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 3. A WTP Radio Information message element carried in the Discovery, + Primary Discovery, and Join Request and Response messages, + indicating the binding-specific radio types supported at the WTP + and AC. + + If technology-specific message elements are required for any of the + existing CAPWAP messages defined in this specification, they MUST + also be defined in the technology binding document. + + The naming of binding-specific message elements MUST begin with the + name of the technology type, e.g., the binding for IEEE 802.11, + provided in [RFC5416], begins with "IEEE 802.11". + + The CAPWAP binding concept MUST also be used in any future + specifications that add functionality to either the base CAPWAP + protocol specification, or any published CAPWAP binding + specification. A separate WTP Radio Information message element MUST + be created to properly advertise support for the specification. This + mechanism allows for future protocol extensibility, while providing + the necessary capabilities advertisement, through the WTP Radio + Information message element, to ensure WTP/AC interoperability. + +2.2. CAPWAP Session Establishment Overview + + This section describes the session establishment process message + exchanges between a CAPWAP WTP and AC. The annotated ladder diagram + shows the AC on the right, the WTP on the left, and assumes the use + of certificates for DTLS authentication. The CAPWAP protocol state + machine is described in detail in Section 2.3. Note that DTLS allows + certain messages to be aggregated into a single frame, which is + denoted via an asterisk in Figure 3. + + ============ ============ + WTP AC + ============ ============ + [----------- begin optional discovery ------------] + + Discover Request + ------------------------------------> + Discover Response + <------------------------------------ + + [----------- end optional discovery ------------] + + (-- begin DTLS handshake --) + + ClientHello + ------------------------------------> + + + +Calhoun, et al. Standards Track [Page 13] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + HelloVerifyRequest (with cookie) + <------------------------------------ + + + ClientHello (with cookie) + ------------------------------------> + ServerHello, + Certificate, + ServerHelloDone* + <------------------------------------ + + (-- WTP callout for AC authorization --) + + Certificate (optional), + ClientKeyExchange, + CertificateVerify (optional), + ChangeCipherSpec, + Finished* + ------------------------------------> + + (-- AC callout for WTP authorization --) + + ChangeCipherSpec, + Finished* + <------------------------------------ + + (-- DTLS session is established now --) + + Join Request + ------------------------------------> + Join Response + <------------------------------------ + [-- Join State Complete --] + + (-- assume image is up to date --) + + Configuration Status Request + ------------------------------------> + Configuration Status Response + <------------------------------------ + [-- Configure State Complete --] + + Change State Event Request + ------------------------------------> + Change State Event Response + <------------------------------------ + [-- Data Check State Complete --] + + + + +Calhoun, et al. Standards Track [Page 14] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + (-- enter RUN state --) + + : + : + + Echo Request + ------------------------------------> + Echo Response + <------------------------------------ + + : + : + + Event Request + ------------------------------------> + Event Response + <------------------------------------ + + : + : + + Figure 3: CAPWAP Control Protocol Exchange + + At the end of the illustrated CAPWAP message exchange, the AC and WTP + are securely exchanging CAPWAP Control messages. This illustration + is provided to clarify protocol operation, and does not include any + possible error conditions. Section 2.3 provides a detailed + description of the corresponding state machine. + +2.3. CAPWAP State Machine Definition + + The following state diagram represents the lifecycle of a WTP-AC + session. Use of DTLS by the CAPWAP protocol results in the + juxtaposition of two nominally separate yet tightly bound state + machines. The DTLS and CAPWAP state machines are coupled through an + API consisting of commands (see Section 2.3.2.1) and notifications + (see Section 2.3.2.2). Certain transitions in the DTLS state machine + are triggered by commands from the CAPWAP state machine, while + certain transitions in the CAPWAP state machine are triggered by + notifications from the DTLS state machine. + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 15] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + /-------------------------------------\ + | /-------------------------\| + | p| || + | q+----------+ r +------------+ || + | | Run |-->| Reset |-\|| + | +----------+ +------------+ ||| + n| o ^ ^ ^ s||| + +------------+--------/ | | ||| + | Data Check | /-------/ | ||| + +------------+<-------\ | | ||| + | | | ||| + /------------------+--------\ | ||| + f| m| h| j v k| ||| + +--------+ +-----------+ +--------------+||| + | Join |---->| Configure | | Image Data |||| + +--------+ n +-----------+ +--------------+||| + ^ |g i| l| ||| + | | \-------------------\ | ||| + | \--------------------------------------\| | ||| + \------------------------\ || | ||| + /--------------<----------------+---------------\ || | ||| + | /------------<----------------+-------------\ | || | ||| + | | 4 |d t| | vv v vvv + | | +----------------+ +--------------+ +-----------+ + | | | DTLS Setup | | DTLS Connect |-->| DTLS TD | + /-|-|---+----------------+ +--------------+ e +-----------+ + | | | |$ ^ ^ |5 ^6 ^ ^ |w + v v v | | | | \-------\ | | | + | | | | | | \---------\ | | /-----------/ | + | | | | | \--\ | | | | | + | | | | | | | | | | | + | | | v 3| 1 |% # v | |a |b v + | | \->+------+-->+------+ +-----------+ +--------+ + | | | Idle | | Disc | | Authorize | | Dead | + | | +------+<--+------+ +-----------+ +--------+ + | | ^ 0^ 2 |! + | | | | | +-------+ + *| |u | \---------+---| Start | + | | |@ | +-------+ + | \->+---------+<------/ + \--->| Sulking | + +---------+& + + Figure 4: CAPWAP Integrated State Machine + + The CAPWAP protocol state machine, depicted above, is used by both + the AC and the WTP. In cases where states are not shared (i.e., not + implemented in one or the other of the AC or WTP), this is explicitly + + + +Calhoun, et al. Standards Track [Page 16] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + called out in the transition descriptions below. For every state + defined, only certain messages are permitted to be sent and received. + The CAPWAP Control message definitions specify the state(s) in which + each message is valid. + + Since the WTP only communicates with a single AC, it only has a + single instance of the CAPWAP state machine. The state machine works + differently on the AC since it communicates with many WTPs. The AC + uses the concept of three threads. Note that the term thread used + here does not necessarily imply that implementers must use threads, + but it is one possible way of implementing the AC's state machine. + + Listener Thread: The AC's Listener thread handles inbound DTLS + session establishment requests, through the DTLSListen command. + Upon creation, the Listener thread starts in the DTLS Setup state. + Once a DTLS session has been validated, which occurs when the + state machine enters the "Authorize" state, the Listener thread + creates a WTP session-specific Service thread and state context. + The state machine transitions in Figure 4 are represented by + numerals. It is necessary for the AC to protect itself against + various attacks that exist with non-authenticated frames. See + Section 12 for more information. + + Discovery Thread: The AC's Discovery thread is responsible for + receiving, and responding to, Discovery Request messages. The + state machine transitions in Figure 4 are represented by numerals. + Note that the Discovery thread does not maintain any per-WTP- + specific context information, and a single state context exists. + It is necessary for the AC to protect itself against various + attacks that exist with non-authenticated frames. See Section 12 + for more information. + + Service Thread: The AC's Service thread handles the per-WTP states, + and one such thread exists per-WTP connection. This thread is + created by the Listener thread when the Authorize state is + reached. When created, the Service thread inherits a copy of the + state machine context from the Listener thread. When + communication with the WTP is complete, the Service thread is + terminated and all associated resources are released. The state + machine transitions in Figure 4 are represented by alphabetic and + punctuation characters. + +2.3.1. CAPWAP Protocol State Transitions + + This section describes the various state transitions, and the events + that cause them. This section does not discuss interactions between + DTLS- and CAPWAP-specific states. Those interactions, and DTLS- + specific states and transitions, are discussed in Section 2.3.2. + + + +Calhoun, et al. Standards Track [Page 17] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Start to Idle (0): This transition occurs once device initialization + is complete. + + WTP: This state transition is used to start the WTP's CAPWAP + state machine. + + AC: The AC creates the Discovery and Listener threads and starts + the CAPWAP state machine. + + Idle to Discovery (1): This transition occurs to support the CAPWAP + discovery process. + + WTP: The WTP enters the Discovery state prior to transmitting the + first Discovery Request message (see Section 5.1). Upon + entering this state, the WTP sets the DiscoveryInterval + timer (see Section 4.7). The WTP resets the DiscoveryCount + counter to zero (0) (see Section 4.8). The WTP also clears + all information from ACs it may have received during a + previous Discovery phase. + + AC: This state transition is executed by the AC's Discovery + thread, and occurs when a Discovery Request message is + received. The AC SHOULD respond with a Discovery Response + message (see Section 5.2). + + Discovery to Discovery (#): In the Discovery state, the WTP + determines to which AC to connect. + + WTP: This transition occurs when the DiscoveryInterval timer + expires. If the WTP is configured with a list of ACs, it + transmits a Discovery Request message to every AC from which + it has not received a Discovery Response message. For every + transition to this event, the WTP increments the + DiscoveryCount counter. See Section 5.1 for more + information on how the WTP knows the ACs to which it should + transmit the Discovery Request messages. The WTP restarts + the DiscoveryInterval timer whenever it transmits Discovery + Request messages. + + AC: This is an invalid state transition for the AC. + + Discovery to Idle (2): This transition occurs on the AC's Discovery + thread when the Discovery processing is complete. + + WTP: This is an invalid state transition for the WTP. + + + + + + +Calhoun, et al. Standards Track [Page 18] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + AC: This state transition is executed by the AC's Discovery + thread when it has transmitted the Discovery Response, in + response to a Discovery Request. + + Discovery to Sulking (!): This transition occurs on a WTP when AC + Discovery fails. + + WTP: The WTP enters this state when the DiscoveryInterval timer + expires and the DiscoveryCount variable is equal to the + MaxDiscoveries variable (see Section 4.8). Upon entering + this state, the WTP MUST start the SilentInterval timer. + While in the Sulking state, all received CAPWAP protocol + messages MUST be ignored. + + AC: This is an invalid state transition for the AC. + + Sulking to Idle (@): This transition occurs on a WTP when it must + restart the Discovery phase. + + WTP: The WTP enters this state when the SilentInterval timer (see + Section 4.7) expires. The FailedDTLSSessionCount, + DiscoveryCount, and FailedDTLSAuthFailCount counters are + reset to zero. + + AC: This is an invalid state transition for the AC. + + Sulking to Sulking (&): The Sulking state provides the silent + period, minimizing the possibility for Denial-of-Service (DoS) + attacks. + + WTP: All packets received from the AC while in the sulking state + are ignored. + + AC: This is an invalid state transition for the AC. + + Idle to DTLS Setup (3): This transition occurs to establish a secure + DTLS session with the peer. + + WTP: The WTP initiates this transition by invoking the DTLSStart + command (see Section 2.3.2.1), which starts the DTLS session + establishment with the chosen AC and the WaitDTLS timer is + started (see Section 4.7). When the Discovery phase is + bypassed, it is assumed the WTP has locally configured ACs. + + + + + + + + +Calhoun, et al. Standards Track [Page 19] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + AC: Upon entering the Idle state from the Start state, the newly + created Listener thread automatically transitions to the + DTLS Setup and invokes the DTLSListen command (see + Section 2.3.2.1), and the WaitDTLS timer is started (see + Section 4.7). + + Discovery to DTLS Setup (%): This transition occurs to establish a + secure DTLS session with the peer. + + WTP: The WTP initiates this transition by invoking the DTLSStart + command (see Section 2.3.2.1), which starts the DTLS session + establishment with the chosen AC. The decision of to which + AC to connect is the result of the Discovery phase, which is + described in Section 3.3. + + AC: This is an invalid state transition for the AC. + + DTLS Setup to Idle ($): This transition occurs when the DTLS + connection setup fails. + + WTP: The WTP initiates this state transition when it receives a + DTLSEstablishFail notification from DTLS (see + Section 2.3.2.2), and the FailedDTLSSessionCount or the + FailedDTLSAuthFailCount counter have not reached the value + of the MaxFailedDTLSSessionRetry variable (see Section 4.8). + This error notification aborts the secure DTLS session + establishment. When this notification is received, the + FailedDTLSSessionCount counter is incremented. This state + transition also occurs if the WaitDTLS timer has expired. + + AC: This is an invalid state transition for the AC. + + DTLS Setup to Sulking (*): This transition occurs when repeated + attempts to set up the DTLS connection have failed. + + WTP: The WTP enters this state when the FailedDTLSSessionCount or + the FailedDTLSAuthFailCount counter reaches the value of the + MaxFailedDTLSSessionRetry variable (see Section 4.8). Upon + entering this state, the WTP MUST start the SilentInterval + timer. While in the Sulking state, all received CAPWAP and + DTLS protocol messages received MUST be ignored. + + AC: This is an invalid state transition for the AC. + + DTLS Setup to DTLS Setup (4): This transition occurs when the DTLS + Session failed to be established. + + WTP: This is an invalid state transition for the WTP. + + + +Calhoun, et al. Standards Track [Page 20] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + AC: The AC's Listener initiates this state transition when it + receives a DTLSEstablishFail notification from DTLS (see + Section 2.3.2.2). This error notification aborts the secure + DTLS session establishment. When this notification is + received, the FailedDTLSSessionCount counter is incremented. + The Listener thread then invokes the DTLSListen command (see + Section 2.3.2.1). + + DTLS Setup to Authorize (5): This transition occurs when an incoming + DTLS session is being established, and the DTLS stack needs + authorization to proceed with the session establishment. + + WTP: This state transition occurs when the WTP receives the + DTLSPeerAuthorize notification (see Section 2.3.2.2). Upon + entering this state, the WTP performs an authorization check + against the AC credentials. See Section 2.4.4 for more + information on AC authorization. + + AC: This state transition is handled by the AC's Listener thread + when the DTLS module initiates the DTLSPeerAuthorize + notification (see Section 2.3.2.2). The Listener thread + forks an instance of the Service thread, along with a copy + of the state context. Once created, the Service thread + performs an authorization check against the WTP credentials. + See Section 2.4.4 for more information on WTP authorization. + + Authorize to DTLS Setup (6): This transition is executed by the + Listener thread to enable it to listen for new incoming sessions. + + WTP: This is an invalid state transition for the WTP. + + AC: This state transition occurs when the AC's Listener thread + has created the WTP context and the Service thread. The + Listener thread then invokes the DTLSListen command (see + Section 2.3.2.1). + + Authorize to DTLS Connect (a): This transition occurs to notify the + DTLS stack that the session should be established. + + WTP: This state transition occurs when the WTP has successfully + authorized the AC's credentials (see Section 2.4.4). This + is done by invoking the DTLSAccept DTLS command (see + Section 2.3.2.1). + + AC: This state transition occurs when the AC has successfully + authorized the WTP's credentials (see Section 2.4.4). This + is done by invoking the DTLSAccept DTLS command (see + Section 2.3.2.1). + + + +Calhoun, et al. Standards Track [Page 21] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Authorize to DTLS Teardown (b): This transition occurs to notify the + DTLS stack that the session should be aborted. + + WTP: This state transition occurs when the WTP has been unable to + authorize the AC, using the AC credentials. The WTP then + aborts the DTLS session by invoking the DTLSAbortSession + command (see Section 2.3.2.1). This state transition also + occurs if the WaitDTLS timer has expired. The WTP starts + the DTLSSessionDelete timer (see Section 4.7.6). + + AC: This state transition occurs when the AC has been unable to + authorize the WTP, using the WTP credentials. The AC then + aborts the DTLS session by invoking the DTLSAbortSession + command (see Section 2.3.2.1). This state transition also + occurs if the WaitDTLS timer has expired. The AC starts the + DTLSSessionDelete timer (see Section 4.7.6). + + DTLS Connect to DTLS Teardown (c): This transition occurs when the + DTLS Session failed to be established. + + WTP: This state transition occurs when the WTP receives either a + DTLSAborted or DTLSAuthenticateFail notification (see + Section 2.3.2.2), indicating that the DTLS session was not + successfully established. When this transition occurs due + to the DTLSAuthenticateFail notification, the + FailedDTLSAuthFailCount is incremented; otherwise, the + FailedDTLSSessionCount counter is incremented. This state + transition also occurs if the WaitDTLS timer has expired. + The WTP starts the DTLSSessionDelete timer (see + Section 4.7.6). + + AC: This state transition occurs when the AC receives either a + DTLSAborted or DTLSAuthenticateFail notification (see + Section 2.3.2.2), indicating that the DTLS session was not + successfully established, and both of the + FailedDTLSAuthFailCount and FailedDTLSSessionCount counters + have not reached the value of the MaxFailedDTLSSessionRetry + variable (see Section 4.8). This state transition also + occurs if the WaitDTLS timer has expired. The AC starts the + DTLSSessionDelete timer (see Section 4.7.6). + + DTLS Connect to Join (d): This transition occurs when the DTLS + Session is successfully established. + + WTP: This state transition occurs when the WTP receives the + DTLSEstablished notification (see Section 2.3.2.2), + indicating that the DTLS session was successfully + established. When this notification is received, the + + + +Calhoun, et al. Standards Track [Page 22] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + FailedDTLSSessionCount counter is set to zero. The WTP + enters the Join state by transmitting the Join Request to + the AC. The WTP stops the WaitDTLS timer. + + AC: This state transition occurs when the AC receives the + DTLSEstablished notification (see Section 2.3.2.2), + indicating that the DTLS session was successfully + established. When this notification is received, the + FailedDTLSSessionCount counter is set to zero. The AC stops + the WaitDTLS timer, and starts the WaitJoin timer. + + Join to DTLS Teardown (e): This transition occurs when the join + process has failed. + + WTP: This state transition occurs when the WTP receives a Join + Response message with a Result Code message element + containing an error, or if the Image Identifier provided by + the AC in the Join Response message differs from the WTP's + currently running firmware version and the WTP has the + requested image in its non-volatile memory. This causes the + WTP to initiate the DTLSShutdown command (see + Section 2.3.2.1). This transition also occurs if the WTP + receives one of the following DTLS notifications: + DTLSAborted, DTLSReassemblyFailure, or DTLSPeerDisconnect. + The WTP starts the DTLSSessionDelete timer (see + Section 4.7.6). + + AC: This state transition occurs either if the WaitJoin timer + expires or if the AC transmits a Join Response message with + a Result Code message element containing an error. This + causes the AC to initiate the DTLSShutdown command (see + Section 2.3.2.1). This transition also occurs if the AC + receives one of the following DTLS notifications: + DTLSAborted, DTLSReassemblyFailure, or DTLSPeerDisconnect. + The AC starts the DTLSSessionDelete timer (see + Section 4.7.6). + + Join to Image Data (f): This state transition is used by the WTP and + the AC to download executable firmware. + + WTP: The WTP enters the Image Data state when it receives a + successful Join Response message and determines that the + software version in the Image Identifier message element is + not the same as its currently running image. The WTP also + detects that the requested image version is not currently + available in the WTP's non-volatile storage (see Section 9.1 + for a full description of the firmware download process). + The WTP initializes the EchoInterval timer (see + + + +Calhoun, et al. Standards Track [Page 23] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Section 4.7), and transmits the Image Data Request message + (see Section 9.1.1) requesting the start of the firmware + download. + + AC: This state transition occurs when the AC receives the Image + Data Request message from the WTP, after having sent its + Join Response to the WTP. The AC stops the WaitJoin timer. + The AC MUST transmit an Image Data Response message (see + Section 9.1.2) to the WTP, which includes a portion of the + firmware. + + Join to Configure (g): This state transition is used by the WTP and + the AC to exchange configuration information. + + WTP: The WTP enters the Configure state when it receives a + successful Join Response message, and determines that the + included Image Identifier message element is the same as its + currently running image. The WTP transmits the + Configuration Status Request message (see Section 8.2) to + the AC with message elements describing its current + configuration. + + AC: This state transition occurs when it receives the + Configuration Status Request message from the WTP (see + Section 8.2), which MAY include specific message elements to + override the WTP's configuration. The AC stops the WaitJoin + timer. The AC transmits the Configuration Status Response + message (see Section 8.3) and starts the + ChangeStatePendingTimer timer (see Section 4.7). + + Configure to Reset (h): This state transition is used to reset the + connection either due to an error during the configuration phase, + or when the WTP determines it needs to reset in order for the new + configuration to take effect. The CAPWAP Reset command is used to + indicate to the peer that it will initiate a DTLS teardown. + + WTP: The WTP enters the Reset state when it receives a + Configuration Status Response message indicating an error or + when it determines that a reset of the WTP is required, due + to the characteristics of a new configuration. + + AC: The AC transitions to the Reset state when it receives a + Change State Event message from the WTP that contains an + error for which AC policy does not permit the WTP to provide + service. This state transition also occurs when the AC + ChangeStatePendingTimer timer expires. + + + + + +Calhoun, et al. Standards Track [Page 24] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Configure to DTLS Teardown (i): This transition occurs when the + configuration process aborts due to a DTLS error. + + WTP: The WTP enters this state when it receives one of the + following DTLS notifications: DTLSAborted, + DTLSReassemblyFailure, or DTLSPeerDisconnect (see + Section 2.3.2.2). The WTP MAY tear down the DTLS session if + it receives frequent DTLSDecapFailure notifications. The + WTP starts the DTLSSessionDelete timer (see Section 4.7.6). + + AC: The AC enters this state when it receives one of the + following DTLS notifications: DTLSAborted, + DTLSReassemblyFailure, or DTLSPeerDisconnect (see + Section 2.3.2.2). The AC MAY tear down the DTLS session if + it receives frequent DTLSDecapFailure notifications. The AC + starts the DTLSSessionDelete timer (see Section 4.7.6). + + Image Data to Image Data (j): The Image Data state is used by the + WTP and the AC during the firmware download phase. + + WTP: The WTP enters the Image Data state when it receives an + Image Data Response message indicating that the AC has more + data to send. This state transition also occurs when the + WTP receives the subsequent Image Data Requests, at which + time it resets the ImageDataStartTimer time to ensure it + receives the next expected Image Data Request from the AC. + This state transition can also occur when the WTP's + EchoInterval timer (see Section 4.7.7) expires, in which + case the WTP transmits an Echo Request message (see + Section 7.1), and resets its EchoInterval timer. The state + transition also occurs when the WTP receives an Echo + Response from the AC (see Section 7.2). + + AC: This state transition occurs when the AC receives the Image + Data Response message from the WTP while already in the + Image Data state. This state transition also occurs when + the AC receives an Echo Request (see Section 7.1) from the + WTP, in which case it responds with an Echo Response (see + Section 7.2), and resets its EchoInterval timer (see + Section 4.7.7). + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 25] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Image Data to Reset (k): This state transition is used to reset the + DTLS connection prior to restarting the WTP after an image + download. + + WTP: When an image download completes, or if the + ImageDataStartTimer timer expires, the WTP enters the Reset + state. The WTP MAY also transition to this state upon + receiving an Image Data Response message from the AC (see + Section 9.1.2) indicating a failure. + + AC: The AC enters the Reset state either when the image transfer + has successfully completed or an error occurs during the + image download process. + + Image Data to DTLS Teardown (l): This transition occurs when the + firmware download process aborts due to a DTLS error. + + WTP: The WTP enters this state when it receives one of the + following DTLS notifications: DTLSAborted, + DTLSReassemblyFailure, or DTLSPeerDisconnect (see + Section 2.3.2.2). The WTP MAY tear down the DTLS session if + it receives frequent DTLSDecapFailure notifications. The + WTP starts the DTLSSessionDelete timer (see Section 4.7.6). + + AC: The AC enters this state when it receives one of the + following DTLS notifications: DTLSAborted, + DTLSReassemblyFailure, or DTLSPeerDisconnect (see + Section 2.3.2.2). The AC MAY tear down the DTLS session if + it receives frequent DTLSDecapFailure notifications. The AC + starts the DTLSSessionDelete timer (see Section 4.7.6). + + Configure to Data Check (m): This state transition occurs when the + WTP and AC confirm the configuration. + + WTP: The WTP enters this state when it receives a successful + Configuration Status Response message from the AC. The WTP + transmits the Change State Event Request message (see + Section 8.6). + + AC: This state transition occurs when the AC receives the Change + State Event Request message (see Section 8.6) from the WTP. + The AC responds with a Change State Event Response message + (see Section 8.7). The AC MUST start the DataCheckTimer + timer and stops the ChangeStatePendingTimer timer (see + Section 4.7). + + Data Check to DTLS Teardown (n): This transition occurs when the WTP + does not complete the Data Check exchange. + + + +Calhoun, et al. Standards Track [Page 26] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + WTP: This state transition occurs if the WTP does not receive the + Change State Event Response message before a CAPWAP + retransmission timeout occurs. The WTP also transitions to + this state if the underlying reliable transport's + RetransmitCount counter has reached the MaxRetransmit + variable (see Section 4.7). The WTP starts the + DTLSSessionDelete timer (see Section 4.7.6). + + AC: The AC enters this state when the DataCheckTimer timer + expires (see Section 4.7). The AC starts the + DTLSSessionDelete timer (see Section 4.7.6). + + Data Check to Run (o): This state transition occurs when the linkage + between the control and data channels is established, causing the + WTP and AC to enter their normal state of operation. + + WTP: The WTP enters this state when it receives a successful + Change State Event Response message from the AC. The WTP + initiates the data channel, which MAY require the + establishment of a DTLS session, starts the + DataChannelKeepAlive timer (see Section 4.7.2) and transmits + a Data Channel Keep-Alive packet (see Section 4.4.1). The + WTP then starts the EchoInterval timer and + DataChannelDeadInterval timer (see Section 4.7). + + AC: This state transition occurs when the AC receives the Data + Channel Keep-Alive packet (see Section 4.4.1), with a + Session ID message element matching that included by the WTP + in the Join Request message. The AC disables the + DataCheckTimer timer. Note that if AC policy is to require + the data channel to be encrypted, this process would also + require the establishment of a data channel DTLS session. + Upon receiving the Data Channel Keep-Alive packet, the AC + transmits its own Data Channel Keep Alive packet. + + Run to DTLS Teardown (p): This state transition occurs when an error + has occurred in the DTLS stack, causing the DTLS session to be + torn down. + + WTP: The WTP enters this state when it receives one of the + following DTLS notifications: DTLSAborted, + DTLSReassemblyFailure, or DTLSPeerDisconnect (see + Section 2.3.2.2). The WTP MAY tear down the DTLS session if + it receives frequent DTLSDecapFailure notifications. The + WTP also transitions to this state if the underlying + reliable transport's RetransmitCount counter has reached the + MaxRetransmit variable (see Section 4.7). The WTP starts + the DTLSSessionDelete timer (see Section 4.7.6). + + + +Calhoun, et al. Standards Track [Page 27] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + AC: The AC enters this state when it receives one of the + following DTLS notifications: DTLSAborted, + DTLSReassemblyFailure, or DTLSPeerDisconnect (see + Section 2.3.2.2). The AC MAY tear down the DTLS session if + it receives frequent DTLSDecapFailure notifications. The AC + transitions to this state if the underlying reliable + transport's RetransmitCount counter has reached the + MaxRetransmit variable (see Section 4.7). This state + transition also occurs when the AC's EchoInterval timer (see + Section 4.7.7) expires. The AC starts the DTLSSessionDelete + timer (see Section 4.7.6). + + Run to Run (q): This is the normal state of operation. + + WTP: This is the WTP's normal state of operation. The WTP resets + its EchoInterval timer whenever it transmits a request to + the AC. There are many events that result in this state + transition: + + Configuration Update: The WTP receives a Configuration + Update Request message (see Section 8.4). The WTP + MUST respond with a Configuration Update Response + message (see Section 8.5). + + Change State Event: The WTP receives a Change State Event + Response message, or determines that it must initiate + a Change State Event Request message, as a result of a + failure or change in the state of a radio. + + Echo Request: The WTP sends an Echo Request message + (Section 7.1) or receives the corresponding Echo + Response message, (see Section 7.2) from the AC. When + the WTP receives the Echo Response, it resets its + EchoInterval timer (see Section 4.7.7). + + Clear Config Request: The WTP receives a Clear + Configuration Request message (see Section 8.8) and + MUST generate a corresponding Clear Configuration + Response message (see Section 8.9). The WTP MUST + reset its configuration back to manufacturer defaults. + + WTP Event: The WTP sends a WTP Event Request message, + delivering information to the AC (see Section 9.4). + The WTP receives a WTP Event Response message from the + AC (see Section 9.5). + + + + + + +Calhoun, et al. Standards Track [Page 28] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Data Transfer: The WTP sends a Data Transfer Request or + Data Transfer Response message to the AC (see + Section 9.6). The WTP receives a Data Transfer + Request or Data Transfer Response message from the AC + (see Section 9.6). Upon receipt of a Data Transfer + Request, the WTP transmits a Data Transfer Response to + the AC. + + Station Configuration Request: The WTP receives a Station + Configuration Request message (see Section 10.1), to + which it MUST respond with a Station Configuration + Response message (see Section 10.2). + + AC: This is the AC's normal state of operation. Note that the + receipt of any Request from the WTP causes the AC to reset + its EchoInterval timer (see Section 4.7.7). + + Configuration Update: The AC sends a Configuration Update + Request message (see Section 8.4) to the WTP to update + its configuration. The AC receives a Configuration + Update Response message (see Section 8.5) from the + WTP. + + Change State Event: The AC receives a Change State Event + Request message (see Section 8.6), to which it MUST + respond with the Change State Event Response message + (see Section 8.7). + + Echo Request: The AC receives an Echo Request message (see + Section 7.1), to which it MUST respond with an Echo + Response message (see Section 7.2). + + Clear Config Response: The AC sends a Clear Configuration + Request message (see Section 8.8) to the WTP to clear + its configuration. The AC receives a Clear + Configuration Response message from the WTP (see + Section 8.9). + + WTP Event: The AC receives a WTP Event Request message from + the WTP (see Section 9.4) and MUST generate a + corresponding WTP Event Response message (see + Section 9.5). + + Data Transfer: The AC sends a Data Transfer Request or Data + Transfer Response message to the WTP (see + Section 9.6). The AC receives a Data Transfer Request + + + + + +Calhoun, et al. Standards Track [Page 29] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + or Data Transfer Response message from the WTP (see + Section 9.6). Upon receipt of a Data Transfer + Request, the AC transmits a Data Transfer Response to + the WTP. + + Station Configuration Request: The AC sends a Station + Configuration Request message (see Section 10.1) or + receives the corresponding Station Configuration + Response message (see Section 10.2) from the WTP. + + Run to Reset (r): This state transition is used when either the AC + or WTP tears down the connection. This may occur as part of + normal operation, or due to error conditions. + + WTP: The WTP enters the Reset state when it receives a Reset + Request message from the AC. + + AC: The AC enters the Reset state when it transmits a Reset + Request message to the WTP. + + Reset to DTLS Teardown (s): This transition occurs when the CAPWAP + reset is complete to terminate the DTLS session. + + WTP: This state transition occurs when the WTP transmits a Reset + Response message. The WTP does not invoke the DTLSShutdown + command (see Section 2.3.2.1). The WTP starts the + DTLSSessionDelete timer (see Section 4.7.6). + + AC: This state transition occurs when the AC receives a Reset + Response message. This causes the AC to initiate the + DTLSShutdown command (see Section 2.3.2.1). The AC starts + the DTLSSessionDelete timer (see Section 4.7.6). + + DTLS Teardown to Idle (t): This transition occurs when the DTLS + session has been shut down. + + WTP: This state transition occurs when the WTP has successfully + cleaned up all resources associated with the control plane + DTLS session, or if the DTLSSessionDelete timer (see + Section 4.7.6) expires. The data plane DTLS session is also + shut down, and all resources released, if a DTLS session was + established for the data plane. Any timers set for the + current instance of the state machine are also cleared. + + AC: This is an invalid state transition for the AC. + + + + + + +Calhoun, et al. Standards Track [Page 30] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + DTLS Teardown to Sulking (u): This transition occurs when repeated + attempts to setup the DTLS connection have failed. + + WTP: The WTP enters this state when the FailedDTLSSessionCount or + the FailedDTLSAuthFailCount counter reaches the value of the + MaxFailedDTLSSessionRetry variable (see Section 4.8). Upon + entering this state, the WTP MUST start the SilentInterval + timer. While in the Sulking state, all received CAPWAP and + DTLS protocol messages received MUST be ignored. + + AC: This is an invalid state transition for the AC. + + DTLS Teardown to Dead (w): This transition occurs when the DTLS + session has been shut down. + + WTP: This is an invalid state transition for the WTP. + + AC: This state transition occurs when the AC has successfully + cleaned up all resources associated with the control plane + DTLS session , or if the DTLSSessionDelete timer (see + Section 4.7.6) expires. The data plane DTLS session is also + shut down, and all resources released, if a DTLS session was + established for the data plane. Any timers set for the + current instance of the state machine are also cleared. The + AC's Service thread is terminated. + +2.3.2. CAPWAP/DTLS Interface + + This section describes the DTLS Commands used by CAPWAP, and the + notifications received from DTLS to the CAPWAP protocol stack. + +2.3.2.1. CAPWAP to DTLS Commands + + Six commands are defined for the CAPWAP to DTLS API. These + "commands" are conceptual, and may be implemented as one or more + function calls. This API definition is provided to clarify + interactions between the DTLS and CAPWAP components of the integrated + CAPWAP state machine. + + Below is a list of the minimal command APIs: + + o DTLSStart is sent to the DTLS component to cause a DTLS session to + be established. Upon invoking the DTLSStart command, the WaitDTLS + timer is started. The WTP initiates this DTLS command, as the AC + does not initiate DTLS sessions. + + o DTLSListen is sent to the DTLS component to allow the DTLS + component to listen for incoming DTLS session requests. + + + +Calhoun, et al. Standards Track [Page 31] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + o DTLSAccept is sent to the DTLS component to allow the DTLS session + establishment to continue successfully. + + o DTLSAbortSession is sent to the DTLS component to cause the + session that is in the process of being established to be aborted. + This command is also sent when the WaitDTLS timer expires. When + this command is executed, the FailedDTLSSessionCount counter is + incremented. + + o DTLSShutdown is sent to the DTLS component to cause session + teardown. + + o DTLSMtuUpdate is sent by the CAPWAP component to modify the MTU + size used by the DTLS component. See Section 3.5 for more + information on MTU Discovery. The default size is 1468 bytes. + +2.3.2.2. DTLS to CAPWAP Notifications + + DTLS notifications are defined for the DTLS to CAPWAP API. These + "notifications" are conceptual and may be implemented in numerous + ways (e.g., as function return values). This API definition is + provided to clarify interactions between the DTLS and CAPWAP + components of the integrated CAPWAP state machine. It is important + to note that the notifications listed below MAY cause the CAPWAP + state machine to jump from one state to another using a state + transition not listed in Section 2.3.1. When a notification listed + below occurs, the target CAPWAP state shown in Figure 4 becomes the + current state. + + Below is a list of the API notifications: + + o DTLSPeerAuthorize is sent to the CAPWAP component during DTLS + session establishment once the peer's identity has been received. + This notification MAY be used by the CAPWAP component to authorize + the session, based on the peer's identity. The authorization + process will lead to the CAPWAP component initiating either the + DTLSAccept or DTLSAbortSession commands. + + o DTLSEstablished is sent to the CAPWAP component to indicate that a + secure channel now exists, using the parameters provided during + the DTLS initialization process. When this notification is + received, the FailedDTLSSessionCount counter is reset to zero. + When this notification is received, the WaitDTLS timer is stopped. + + o DTLSEstablishFail is sent when the DTLS session establishment has + failed, either due to a local error or due to the peer rejecting + the session establishment. When this notification is received, + the FailedDTLSSessionCount counter is incremented. + + + +Calhoun, et al. Standards Track [Page 32] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + o DTLSAuthenticateFail is sent when DTLS session establishment has + failed due to an authentication error. When this notification is + received, the FailedDTLSAuthFailCount counter is incremented. + + o DTLSAborted is sent to the CAPWAP component to indicate that + session abort (as requested by CAPWAP) is complete; this occurs to + confirm a DTLS session abort or when the WaitDTLS timer expires. + When this notification is received, the WaitDTLS timer is stopped. + + o DTLSReassemblyFailure MAY be sent to the CAPWAP component to + indicate DTLS fragment reassembly failure. + + o DTLSDecapFailure MAY be sent to the CAPWAP module to indicate a + decapsulation failure. DTLSDecapFailure MAY be sent to the CAPWAP + module to indicate an encryption/authentication failure. This + notification is intended for informative purposes only, and is not + intended to cause a change in the CAPWAP state machine (see + Section 12.4). + + o DTLSPeerDisconnect is sent to the CAPWAP component to indicate the + DTLS session has been torn down. Note that this notification is + only received if the DTLS session has been established. + +2.4. Use of DTLS in the CAPWAP Protocol + + DTLS is used as a tightly integrated, secure wrapper for the CAPWAP + protocol. In this document, DTLS and CAPWAP are discussed as + nominally distinct entities; however, they are very closely coupled, + and may even be implemented inseparably. Since there are DTLS + library implementations currently available, and since security + protocols (e.g., IPsec, TLS) are often implemented in widely + available acceleration hardware, it is both convenient and forward- + looking to maintain a modular distinction in this document. + + This section describes a detailed walk-through of the interactions + between the DTLS module and the CAPWAP module, via 'commands' (CAPWAP + to DTLS) and 'notifications' (DTLS to CAPWAP) as they would be + encountered during the normal course of operation. + +2.4.1. DTLS Handshake Processing + + Details of the DTLS handshake process are specified in [RFC4347]. + This section describes the interactions between the DTLS session + establishment process and the CAPWAP protocol. Note that the + conceptual DTLS state is shown below to help understand the point at + which the DTLS states transition. In the normal case, the DTLS + handshake will proceed as shown in Figure 5. (NOTE: this example + uses certificates, but pre-shared keys are also supported.) + + + +Calhoun, et al. Standards Track [Page 33] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + ============ ============ + WTP AC + ============ ============ + ClientHello ------> + <------ HelloVerifyRequest + (with cookie) + + ClientHello ------> + (with cookie) + <------ ServerHello + <------ Certificate + <------ ServerHelloDone + + (WTP callout for AC authorization + occurs in CAPWAP Auth state) + + Certificate* + ClientKeyExchange + CertificateVerify* + ChangeCipherSpec + Finished ------> + + (AC callout for WTP authorization + occurs in CAPWAP Auth state) + + ChangeCipherSpec + <------ Finished + + Figure 5: DTLS Handshake + + DTLS, as specified, provides its own retransmit timers with an + exponential back-off. [RFC4347] does not specify how long + retransmissions should continue. Consequently, timing out incomplete + DTLS handshakes is entirely the responsibility of the CAPWAP module. + + The DTLS implementation used by CAPWAP MUST support TLS Session + Resumption. Session resumption is typically used to establish the + DTLS session used for the data channel. Since the data channel uses + different port numbers than the control channel, the DTLS + implementation on the WTP MUST provide an interface that allows the + CAPWAP module to request session resumption despite the use of the + different port numbers (TLS implementations usually attempt session + resumption only when connecting to the same IP address and port + number). Note that session resumption is not guaranteed to occur, + and a full DTLS handshake may occur instead. + + + + + + +Calhoun, et al. Standards Track [Page 34] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + The DTLS implementation used by CAPWAP MUST use replay detection, per + Section 3.3 of [RFC4347]. Since the CAPWAP protocol handles + retransmissions by re-encrypting lost frames, any duplicate DTLS + frames are either unintentional or malicious and should be silently + discarded. + +2.4.2. DTLS Session Establishment + + The WTP, either through the Discovery process or through pre- + configuration, determines to which AC to connect. The WTP uses the + DTLSStart command to request that a secure connection be established + to the selected AC. Prior to initiation of the DTLS handshake, the + WTP sets the WaitDTLS timer. Upon invoking the DTLSStart or + DTLSListen commands, the WTP and AC, respectively, set the WaitDTLS + timer. If the DTLSEstablished notification is not received prior to + timer expiration, the DTLS session is aborted by issuing the + DTLSAbortSession DTLS command. This notification causes the CAPWAP + module to transition to the Idle state. Upon receiving a + DTLSEstablished notification, the WaitDTLS timer is deactivated. + +2.4.3. DTLS Error Handling + + If the AC or WTP does not respond to any DTLS handshake messages sent + by its peer, the DTLS specification calls for the message to be + retransmitted. Note that during the handshake, when both the AC and + the WTP are expecting additional handshake messages, they both + retransmit if an expected message has not been received (note that + retransmissions for CAPWAP Control messages work differently: all + CAPWAP Control messages are either requests or responses, and the + peer who sent the request is responsible for retransmissions). + + If the WTP or the AC does not receive an expected DTLS handshake + message despite of retransmissions, the WaitDTLS timer will + eventually expire, and the session will be terminated. This can + happen if communication between the peers has completely failed, or + if one of the peers sent a DTLS Alert message that was lost in + transit (DTLS does not retransmit Alert messages). + + If a cookie fails to validate, this could represent a WTP error, or + it could represent a DoS attack. Hence, AC resource utilization + SHOULD be minimized. The AC MAY log a message indicating the + failure, and SHOULD treat the message as though no cookie were + present. + + Since DTLS Handshake messages are potentially larger than the maximum + record size, DTLS supports fragmenting of Handshake messages across + multiple records. There are several potential causes of re-assembly + + + + +Calhoun, et al. Standards Track [Page 35] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + errors, including overlapping and/or lost fragments. The DTLS + component MUST send a DTLSReassemblyFailure notification to the + CAPWAP component. Whether precise information is given along with + notification is an implementation issue, and hence is beyond the + scope of this document. Upon receipt of such an error, the CAPWAP + component SHOULD log an appropriate error message. Whether + processing continues or the DTLS session is terminated is + implementation dependent. + + DTLS decapsulation errors consist of three types: decryption errors, + authentication errors, and malformed DTLS record headers. Since DTLS + authenticates the data prior to encapsulation, if decryption fails, + it is difficult to detect this without first attempting to + authenticate the packet. If authentication fails, a decryption error + is also likely, but not guaranteed. Rather than attempt to derive + (and require the implementation of) algorithms for detecting + decryption failures, decryption failures are reported as + authentication failures. The DTLS component MUST provide a + DTLSDecapFailure notification to the CAPWAP component when such + errors occur. If a malformed DTLS record header is detected, the + packets SHOULD be silently discarded, and the receiver MAY log an + error message. + + There is currently only one encapsulation error defined: MTU + exceeded. As part of DTLS session establishment, the CAPWAP + component informs the DTLS component of the MTU size. This may be + dynamically modified at any time when the CAPWAP component sends the + DTLSMtuUpdate command to the DTLS component (see Section 2.3.2.1). + The value provided to the DTLS stack is the result of the MTU + Discovery process, which is described in Section 3.5. The DTLS + component returns this notification to the CAPWAP component whenever + a transmission request will result in a packet that exceeds the MTU. + +2.4.4. DTLS Endpoint Authentication and Authorization + + DTLS supports endpoint authentication with certificates or pre-shared + keys. The TLS algorithm suites for each endpoint authentication + method are described below. + +2.4.4.1. Authenticating with Certificates + + CAPWAP implementations only use cipher suites that are recommended + for use with DTLS, see [DTLS-DESIGN]. At present, the following + algorithms MUST be supported when using certificates for CAPWAP + authentication: + + o TLS_RSA_WITH_AES_128_CBC_SHA [RFC5246] + + + + +Calhoun, et al. Standards Track [Page 36] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + The following algorithms SHOULD be supported when using certificates: + + o TLS_DHE_RSA_WITH_AES_128_CBC_SHA [RFC5246] + + The following algorithms MAY be supported when using certificates: + + o TLS_RSA_WITH_AES_256_CBC_SHA [RFC5246] + + o TLS_DHE_RSA_WITH_AES_256_CBC_SHA [RFC5246] + + Additional ciphers MAY be defined in subsequent CAPWAP + specifications. + +2.4.4.2. Authenticating with Pre-Shared Keys + + Pre-shared keys present significant challenges from a security + perspective, and for that reason, their use is strongly discouraged. + Several methods for authenticating with pre-shared keys are defined + [RFC4279], and we focus on the following two: + + o Pre-Shared Key (PSK) key exchange algorithm - simplest method, + ciphersuites use only symmetric key algorithms. + + o DHE_PSK key exchange algorithm - use a PSK to authenticate a + Diffie-Hellman exchange. These ciphersuites give some additional + protection against dictionary attacks and also provide Perfect + Forward Secrecy (PFS). + + The first approach (plain PSK) is susceptible to passive dictionary + attacks; hence, while this algorithm MUST be supported, special care + should be taken when choosing that method. In particular, user- + readable passphrases SHOULD NOT be used, and use of short PSKs SHOULD + be strongly discouraged. + + The following cryptographic algorithms MUST be supported when using + pre-shared keys: + + o TLS_PSK_WITH_AES_128_CBC_SHA [RFC5246] + + o TLS_DHE_PSK_WITH_AES_128_CBC_SHA [RFC5246] + + The following algorithms MAY be supported when using pre-shared keys: + + o TLS_PSK_WITH_AES_256_CBC_SHA [RFC5246] + + o TLS_DHE_PSK_WITH_AES_256_CBC_SHA [RFC5246] + + Additional ciphers MAY be defined in following CAPWAP specifications. + + + +Calhoun, et al. Standards Track [Page 37] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +2.4.4.3. Certificate Usage + + Certificate authorization by the AC and WTP is required so that only + an AC may perform the functions of an AC and that only a WTP may + perform the functions of a WTP. This restriction of functions to the + AC or WTP requires that the certificates used by the AC MUST be + distinguishable from the certificate used by the WTP. To accomplish + this differentiation, the x.509 certificates MUST include the + Extended Key Usage (EKU) certificate extension [RFC5280]. + + The EKU field indicates one or more purposes for which a certificate + may be used. It is an essential part in authorization. Its syntax + is described in [RFC5280] and [ISO.9834-1.1993] and is as follows: + + ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + + KeyPurposeId ::= OBJECT IDENTIFIER + + Here we define two KeyPurposeId values, one for the WTP and one for + the AC. Inclusion of one of these two values indicates a certificate + is authorized for use by a WTP or AC, respectively. These values are + formatted as id-kp fields. + + id-kp OBJECT IDENTIFIER ::= + { iso(1) identified-organization(3) dod(6) internet(1) + security(5) mechanisms(5) pkix(7) 3 } + + id-kp-capwapAC OBJECT IDENTIFIER ::= { id-kp 18 } + + id-kp-capwapWTP OBJECT IDENTIFIER ::= { id-kp 19 } + + All capwap devices MUST support the ExtendedKeyUsage certificate + extension if it is present in a certificate. If the extension is + present, then the certificate MUST have either the id-kp-capwapAC or + the id-kp-anyExtendedKeyUsage keyPurposeID to act as an AC. + Similarly, if the extension is present, a device MUST have the id-kp- + capwapWTP or id-kp-anyExtendedKeyUsage keyPurposeID to act as a WTP. + + Part of the CAPWAP certificate validation process includes ensuring + that the proper EKU is included and allowing the CAPWAP session to be + established only if the extension properly represents the device. + For instance, an AC SHOULD NOT accept a connection request from + another AC, and therefore MUST verify that the id-kp-capwapWTP EKU is + present in the certificate. + + CAPWAP implementations MUST support certificates where the common + name (CN) for both the WTP and AC is the MAC address of that device. + + + + +Calhoun, et al. Standards Track [Page 38] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + The MAC address MUST be encoded in the PrintableString format, using + the well-recognized MAC address format of 01:23:45:67:89:ab. The CN + field MAY contain either of the EUI-48 [EUI-48] or EUI-64 [EUI-64] + MAC Address formats. This seemingly unconventional use of the CN + field is consistent with other standards that rely on device + certificates that are provisioned during the manufacturing process, + such as Packet Cable [PacketCable], Cable Labs [CableLabs], and WiMAX + [WiMAX]. See Section 12.8 for more information on the use of the MAC + address in the CN field. + + ACs and WTPs MUST authorize (e.g., through access control lists) + certificates of devices to which they are connecting, e.g., based on + the issuer, MAC address, or organizational information specified in + the certificate. The identities specified in the certificates bind a + particular DTLS session to a specific pair of mutually authenticated + and authorized MAC addresses. The particulars of authorization + filter construction are implementation details which are, for the + most part, not within the scope of this specification. However, at + minimum, all devices MUST verify that the appropriate EKU bit is set + according to the role of the peer device (AC versus WTP), and that + the issuer of the certificate is appropriate for the domain in + question. + +2.4.4.4. PSK Usage + + When DTLS uses PSK Ciphersuites, the ServerKeyExchange message MUST + contain the "PSK identity hint" field and the ClientKeyExchange + message MUST contain the "PSK identity" field. These fields are used + to help the WTP select the appropriate PSK for use with the AC, and + then indicate to the AC which key is being used. When PSKs are + provisioned to WTPs and ACs, both the PSK Hint and PSK Identity for + the key MUST be specified. + + The PSK Hint SHOULD uniquely identify the AC and the PSK Identity + SHOULD uniquely identify the WTP. It is RECOMMENDED that these hints + and identities be the ASCII HEX-formatted MAC addresses of the + respective devices, since each pairwise combination of WTP and AC + SHOULD have a unique PSK. The PSK Hint and Identity SHOULD be + sufficient to perform authorization, as simply having knowledge of a + PSK does not necessarily imply authorization. + + If a single PSK is being used for multiple devices on a CAPWAP + network, which is NOT RECOMMENDED, the PSK Hint and Identity can no + longer be a MAC address, so appropriate hints and identities SHOULD + be selected to identify the group of devices to which the PSK is + provisioned. + + + + + +Calhoun, et al. Standards Track [Page 39] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +3. CAPWAP Transport + + Communication between a WTP and an AC is established using the + standard UDP client/server model. The CAPWAP protocol supports both + UDP and UDP-Lite [RFC3828] transport protocols. When run over IPv4, + UDP is used for the CAPWAP Control and Data channels. + + When run over IPv6, the CAPWAP Control channel always uses UDP, while + the CAPWAP Data channel may use either UDP or UDP-Lite. UDP-Lite is + the default transport protocol for the CAPWAP Data channel. However, + if a middlebox or IPv4 to IPv6 gateway has been discovered, UDP is + used for the CAPWAP Data channel. + + This section describes how the CAPWAP protocol is carried over IP and + UDP/UDP-Lite transport protocols. The CAPWAP Transport Protocol + message element, Section 4.6.14, describes the rules to use in + determining which transport protocol is to be used. + + In order for CAPWAP to be compatible with potential middleboxes in + the network, CAPWAP implementations MUST send return traffic from the + same port on which they received traffic from a given peer. Further, + any unsolicited requests generated by a CAPWAP node MUST be sent on + the same port. + +3.1. UDP Transport + + One of the CAPWAP protocol requirements is to allow a WTP to reside + behind a middlebox, firewall, and/or Network Address Translation + (NAT) device. Since a CAPWAP session is initiated by the WTP + (client) to the well-known UDP port of the AC (server), the use of + UDP is a logical choice. When CAPWAP is run over IPv4, the UDP + checksum field in CAPWAP packets MUST be set to zero. + + CAPWAP protocol control packets sent from the WTP to the AC use the + CAPWAP Control channel, as defined in Section 1.4. The CAPWAP + control port at the AC is the well-known UDP port 5246. The CAPWAP + control port at the WTP can be any port selected by the WTP. + + CAPWAP protocol data packets sent from the WTP to the AC use the + CAPWAP Data channel, as defined in Section 1.4. The CAPWAP data port + at the AC is the well-known UDP port 5247. If an AC permits the + administrator to change the CAPWAP control port, the CAPWAP data port + MUST be the next consecutive port number. The CAPWAP data port at + the WTP can be any port selected by the WTP. + + + + + + + +Calhoun, et al. Standards Track [Page 40] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +3.2. UDP-Lite Transport + + When CAPWAP is run over IPv6, UDP-Lite is the default transport + protocol, which reduces the checksum processing required for each + packet (compared to the use of UDP over IPv6 [RFC2460]). When UDP- + Lite is used, the checksum field MUST have a coverage of 8 [RFC3828]. + + UDP-Lite uses the same port assignments as UDP. + +3.3. AC Discovery + + The AC Discovery phase allows the WTP to determine which ACs are + available and choose the best AC with which to establish a CAPWAP + session. The Discovery phase occurs when the WTP enters the optional + Discovery state. A WTP does not need to complete the AC Discovery + phase if it uses a pre-configured AC. This section details the + mechanism used by a WTP to dynamically discover candidate ACs. + + A WTP and an AC will frequently not reside in the same IP subnet + (broadcast domain). When this occurs, the WTP must be capable of + discovering the AC, without requiring that multicast services are + enabled in the network. + + When the WTP attempts to establish communication with an AC, it sends + the Discovery Request message and receives the Discovery Response + message from the AC(s). The WTP MUST send the Discovery Request + message to either the limited broadcast IP address (255.255.255.255), + the well-known CAPWAP multicast address (224.0.1.140), or to the + unicast IP address of the AC. For IPv6 networks, since broadcast + does not exist, the use of "All ACs multicast address" (FF0X:0:0:0:0: + 0:0:18C) is used instead. Upon receipt of the Discovery Request + message, the AC sends a Discovery Response message to the unicast IP + address of the WTP, regardless of whether the Discovery Request + message was sent as a broadcast, multicast, or unicast message. + + WTP use of a limited IP broadcast, multicast, or unicast IP address + is implementation dependent. ACs, on the other hand, MUST support + broadcast, multicast, and unicast discovery. + + When a WTP transmits a Discovery Request message to a unicast + address, the WTP must first obtain the IP address of the AC. Any + static configuration of an AC's IP address on the WTP non-volatile + storage is implementation dependent. However, additional dynamic + schemes are possible, for example: + + + + + + + +Calhoun, et al. Standards Track [Page 41] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + DHCP: See [RFC5417] for more information on the use of DHCP to + discover AC IP addresses. + + DNS: The WTP MAY support use of DNS Service Records (SRVs) [RFC2782] + to discover the AC address(es). In this case, the WTP first + obtains (e.g., from local configuration) the correct domain name + suffix (e.g., "example.com") and performs an SRV lookup with + Service name "capwap-control" and Proto "udp". Thus, the name + resolved in DNS would be, e.g., "_capwap- + control._udp.example.com". Note that the SRV record MAY specify a + non-default port number for the control channel; the port number + for the data channel is the next port number (control channel port + + 1). + + An AC MAY also communicate alternative ACs to the WTP within the + Discovery Response message through the AC IPv4 List (see + Section 4.6.2) and AC IPv6 List (see Section 4.6.2). The addresses + provided in these two message elements are intended to help the WTP + discover additional ACs through means other than those listed above. + + The AC Name with Priority message element (see Section 4.6.5) is used + to communicate a list of preferred ACs to the WTP. The WTP SHOULD + attempt to utilize the ACs listed in the order provided by the AC. + The Name-to-IP Address mapping is handled via the Discovery message + exchange, in which the ACs provide their identity in the AC Name (see + Section 4.6.4) message element in the Discovery Response message. + + Once the WTP has received Discovery Response messages from the + candidate ACs, it MAY use other factors to determine the preferred + AC. For instance, each binding defines a WTP Radio Information + message element (see Section 2.1), which the AC includes in Discovery + Response messages. The presence of one or more of these message + elements is used to identify the CAPWAP bindings supported by the AC. + A WTP MAY connect to an AC based on the supported bindings + advertised. + +3.4. Fragmentation/Reassembly + + While fragmentation and reassembly services are provided by IP, the + CAPWAP protocol also provides such services. Environments where the + CAPWAP protocol is used involve firewall, NAT, and "middlebox" + devices, which tend to drop IP fragments to minimize possible DoS + attacks. By providing fragmentation and reassembly at the + application layer, any fragmentation required due to the tunneling + component of the CAPWAP protocol becomes transparent to these + intermediate devices. Consequently, the CAPWAP protocol can be used + in any network topology including firewall, NAT, and middlebox + devices. + + + +Calhoun, et al. Standards Track [Page 42] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + It is important to note that the fragmentation mechanism employed by + CAPWAP has known limitations and deficiencies, which are similar to + those described in [RFC4963]. The limited size of the Fragment ID + field (see Section 4.3) can cause wrapping of the field, and hence + cause fragments from different datagrams to be incorrectly spliced + together (known as "mis-associated"). For example, a 100Mpbs link + with an MTU of 1500 (causing fragmentation at 1450 bytes) would cause + the Fragment ID field wrap in 8 seconds. Consequently, CAPWAP + implementers are warned to properly size their buffers for reassembly + purposes based on the expected wireless technology throughput. + + CAPWAP implementations SHOULD perform MTU Discovery (see + Section 3.5), which can avoid the need for fragmentation. At the + time of writing of this specification, most enterprise switching and + routing infrastructure were capable of supporting "mini-jumbo" frames + (1800 bytes), which eliminates the need for fragmentation (assuming + the station's MTU is 1500 bytes). The need for fragmentation + typically continues to exist when the WTP communicates with the AC + over a Wide Area Network (WAN). Therefore, future versions of the + CAPWAP protocol SHOULD consider either increasing the size of the + Fragment ID field or providing alternative extensions. + +3.5. MTU Discovery + + Once a WTP has discovered the AC with which it wishes to establish a + CAPWAP session, it SHOULD perform a Path MTU (PMTU) discovery. One + recommendation for performing PMTU discovery is to have the WTP + transmit Discovery Request (see Section 5.1) messages, and include + the MTU Discovery Padding message element (see Section 4.6.32). The + actual procedures used for PMTU discovery are described in [RFC1191] + for IPv4; for IPv6, [RFC1981] SHOULD be used. Alternatively, + implementers MAY use the procedures defined in [RFC4821]. The WTP + SHOULD also periodically re-evaluate the PMTU using the guidelines + provided in these two RFCs, using the Primary Discovery Request (see + Section 5.3) along with the MTU Discovery Padding message element + (see Section 4.6.32). When the MTU is initially known, or updated in + the case where an existing session already exists, the discovered + PMTU is used to configure the DTLS component (see Section 2.3.2.1), + while non-DTLS frames need to be fragmented to fit the MTU, defined + in Section 3.4. + +4. CAPWAP Packet Formats + + This section contains the CAPWAP protocol packet formats. A CAPWAP + protocol packet consists of one or more CAPWAP Transport Layer packet + headers followed by a CAPWAP message. The CAPWAP message can be + either of type Control or Data, where Control packets carry + + + + +Calhoun, et al. Standards Track [Page 43] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + signaling, and Data packets carry user payloads. The CAPWAP frame + formats for CAPWAP Data packets, and for DTLS encapsulated CAPWAP + Data and Control packets are defined below. + + The CAPWAP Control protocol includes two messages that are never + protected by DTLS: the Discovery Request message and the Discovery + Response message. These messages need to be in the clear to allow + the CAPWAP protocol to properly identify and process them. The + format of these packets are as follows: + + CAPWAP Control Packet (Discovery Request/Response): + +-------------------------------------------+ + | IP | UDP | CAPWAP | Control | Message | + | Hdr | Hdr | Header | Header | Element(s) | + +-------------------------------------------+ + + All other CAPWAP Control protocol messages MUST be protected via the + DTLS protocol, which ensures that the packets are both authenticated + and encrypted. These packets include the CAPWAP DTLS Header, which + is described in Section 4.2. The format of these packets is as + follows: + + CAPWAP Control Packet (DTLS Security Required): + +------------------------------------------------------------------+ + | IP | UDP | CAPWAP | DTLS | CAPWAP | Control| Message | DTLS | + | Hdr | Hdr | DTLS Hdr | Hdr | Header | Header | Element(s)| Trlr | + +------------------------------------------------------------------+ + \---------- authenticated -----------/ + \------------- encrypted ------------/ + + The CAPWAP protocol allows optional protection of data packets, using + DTLS. Use of data packet protection is determined by AC policy. + When DTLS is utilized, the optional CAPWAP DTLS Header is present, + which is described in Section 4.2. The format of CAPWAP Data packets + is shown below: + + + + + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 44] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + CAPWAP Plain Text Data Packet : + +-------------------------------+ + | IP | UDP | CAPWAP | Wireless | + | Hdr | Hdr | Header | Payload | + +-------------------------------+ + + DTLS Secured CAPWAP Data Packet: + +--------------------------------------------------------+ + | IP | UDP | CAPWAP | DTLS | CAPWAP | Wireless | DTLS | + | Hdr | Hdr | DTLS Hdr | Hdr | Hdr | Payload | Trlr | + +--------------------------------------------------------+ + \------ authenticated -----/ + \------- encrypted --------/ + + UDP Header: All CAPWAP packets are encapsulated within either UDP, + or UDP-Lite when used over IPv6. Section 3 defines the specific + UDP or UDP-Lite usage. + + CAPWAP DTLS Header: All DTLS encrypted CAPWAP protocol packets are + prefixed with the CAPWAP DTLS Header (see Section 4.2). + + DTLS Header: The DTLS Header provides authentication and encryption + services to the CAPWAP payload it encapsulates. This protocol is + defined in [RFC4347]. + + CAPWAP Header: All CAPWAP protocol packets use a common header that + immediately follows the CAPWAP preamble or DTLS Header. The + CAPWAP Header is defined in Section 4.3. + + Wireless Payload: A CAPWAP protocol packet that contains a wireless + payload is a CAPWAP Data packet. The CAPWAP protocol does not + specify the format of the wireless payload, which is defined by + the appropriate wireless standard. Additional information is in + Section 4.4. + + Control Header: The CAPWAP protocol includes a signaling component, + known as the CAPWAP Control protocol. All CAPWAP Control packets + include a Control Header, which is defined in Section 4.5.1. + CAPWAP Data packets do not contain a Control Header field. + + Message Elements: A CAPWAP Control packet includes one or more + message elements, which are found immediately following the + Control Header. These message elements are in a Type/Length/Value + style header, defined in Section 4.6. + + A CAPWAP implementation MUST be capable of receiving a reassembled + CAPWAP message of length 4096 bytes. A CAPWAP implementation MAY + indicate that it supports a higher maximum message length, by + + + +Calhoun, et al. Standards Track [Page 45] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + including the Maximum Message Length message element, see + Section 4.6.31, in the Join Request message or the Join Response + message. + +4.1. CAPWAP Preamble + + The CAPWAP preamble is common to all CAPWAP transport headers and is + used to identify the header type that immediately follows. The + reason for this preamble is to avoid needing to perform byte + comparisons in order to guess whether or not the frame is DTLS + encrypted. It also provides an extensibility framework that can be + used to support additional transport types. The format of the + preamble is as follows: + + 0 + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |Version| Type | + +-+-+-+-+-+-+-+-+ + + Version: A 4-bit field that contains the version of CAPWAP used in + this packet. The value for this specification is zero (0). + + Type: A 4-bit field that specifies the payload type that follows the + UDP header. The following values are supported: + + 0 - CAPWAP Header. The CAPWAP Header (see Section 4.3) + immediately follows the UDP header. If the packet is + received on the CAPWAP Data channel, the CAPWAP stack MUST + treat the packet as a clear text CAPWAP Data packet. If + received on the CAPWAP Control channel, the CAPWAP stack + MUST treat the packet as a clear text CAPWAP Control packet. + If the control packet is not a Discovery Request or + Discovery Response packet, the packet MUST be dropped. + + 1 - CAPWAP DTLS Header. The CAPWAP DTLS Header (and DTLS + packet) immediately follows the UDP header (see + Section 4.2). + +4.2. CAPWAP DTLS Header + + The CAPWAP DTLS Header is used to identify the packet as a DTLS + encrypted packet. The first eight bits include the common CAPWAP + Preamble. The remaining 24 bits are padding to ensure 4-byte + alignment, and MAY be used in a future version of the protocol. The + DTLS packet [RFC4347] always immediately follows this header. The + format of the CAPWAP DTLS Header is as follows: + + + + +Calhoun, et al. Standards Track [Page 46] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |CAPWAP Preamble| Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + CAPWAP Preamble: The CAPWAP Preamble is defined in Section 4.1. The + CAPWAP Preamble's Payload Type field MUST be set to one (1). + + Reserved: The 24-bit field is reserved for future use. All + implementations complying with this protocol MUST set to zero any + bits that are reserved in the version of the protocol supported by + that implementation. Receivers MUST ignore all bits not defined + for the version of the protocol they support. + +4.3. CAPWAP Header + + All CAPWAP protocol messages are encapsulated using a common header + format, regardless of the CAPWAP Control or CAPWAP Data transport + used to carry the messages. However, certain flags are not + applicable for a given transport. Refer to the specific transport + section in order to determine which flags are valid. + + Note that the optional fields defined in this section MUST be present + in the precise order shown below. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |CAPWAP Preamble| HLEN | RID | WBID |T|F|L|W|M|K|Flags| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Fragment ID | Frag Offset |Rsvd | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | (optional) Radio MAC Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | (optional) Wireless Specific Information | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Payload .... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + CAPWAP Preamble: The CAPWAP Preamble is defined in Section 4.1. The + CAPWAP Preamble's Payload Type field MUST be set to zero (0). If + the CAPWAP DTLS Header is present, the version number in both + CAPWAP Preambles MUST match. The reason for this duplicate field + is to avoid any possible tampering of the version field in the + preamble that is not encrypted or authenticated. + + + + + +Calhoun, et al. Standards Track [Page 47] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + HLEN: A 5-bit field containing the length of the CAPWAP transport + header in 4-byte words (similar to IP header length). This length + includes the optional headers. + + RID: A 5-bit field that contains the Radio ID number for this + packet, whose value is between one (1) and 31. Given that MAC + Addresses are not necessarily unique across physical radios in a + WTP, the Radio Identifier (RID) field is used to indicate with + which physical radio the message is associated. + + WBID: A 5-bit field that is the wireless binding identifier. The + identifier will indicate the type of wireless packet associated + with the radio. The following values are defined: + + 0 - Reserved + + 1 - IEEE 802.11 + + 2 - Reserved + + 3 - EPCGlobal [EPCGlobal] + + T: The Type 'T' bit indicates the format of the frame being + transported in the payload. When this bit is set to one (1), the + payload has the native frame format indicated by the WBID field. + When this bit is zero (0), the payload is an IEEE 802.3 frame. + + F: The Fragment 'F' bit indicates whether this packet is a fragment. + When this bit is one (1), the packet is a fragment and MUST be + combined with the other corresponding fragments to reassemble the + complete information exchanged between the WTP and AC. + + L: The Last 'L' bit is valid only if the 'F' bit is set and indicates + whether the packet contains the last fragment of a fragmented + exchange between WTP and AC. When this bit is one (1), the packet + is the last fragment. When this bit is (zero) 0, the packet is + not the last fragment. + + W: The Wireless 'W' bit is used to specify whether the optional + Wireless Specific Information field is present in the header. A + value of one (1) is used to represent the fact that the optional + header is present. + + M: The Radio MAC 'M' bit is used to indicate that the Radio MAC + Address optional header is present. This is used to communicate + the MAC address of the receiving radio. + + + + + +Calhoun, et al. Standards Track [Page 48] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + K: The Keep-Alive 'K' bit indicates the packet is a Data Channel + Keep-Alive packet. This packet is used to map the data channel to + the control channel for the specified Session ID and to maintain + freshness of the data channel. The 'K' bit MUST NOT be set for + data packets containing user data. + + Flags: A set of reserved bits for future flags in the CAPWAP Header. + All implementations complying with this protocol MUST set to zero + any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all bits + not defined for the version of the protocol they support. + + Fragment ID: A 16-bit field whose value is assigned to each group of + fragments making up a complete set. The Fragment ID space is + managed individually for each direction for every WTP/AC pair. + The value of Fragment ID is incremented with each new set of + fragments. The Fragment ID wraps to zero after the maximum value + has been used to identify a set of fragments. + + Fragment Offset: A 13-bit field that indicates where in the payload + this fragment belongs during re-assembly. This field is valid + when the 'F' bit is set to 1. The fragment offset is measured in + units of 8 octets (64 bits). The first fragment has offset zero. + Note that the CAPWAP protocol does not allow for overlapping + fragments. + + Reserved: The 3-bit field is reserved for future use. All + implementations complying with this protocol MUST set to zero any + bits that are reserved in the version of the protocol supported by + that implementation. Receivers MUST ignore all bits not defined + for the version of the protocol they support. + + Radio MAC Address: This optional field contains the MAC address of + the radio receiving the packet. Because the native wireless frame + format to IEEE 802.3 format causes the MAC address of the WTP's + radio to be lost, this field allows the address to be communicated + to the AC. This field is only present if the 'M' bit is set. The + HLEN field assumes 4-byte alignment, and this field MUST be padded + with zeroes (0x00) if it is not 4-byte aligned. + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 49] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + The field contains the basic format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length | MAC Address + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Length: The length of the MAC address field. The formats and + lengths specified in [EUI-48] and [EUI-64] are supported. + + MAC Address: The MAC address of the receiving radio. + + Wireless Specific Information: This optional field contains + technology-specific information that may be used to carry per- + packet wireless information. This field is only present if the + 'W' bit is set. The WBID field in the CAPWAP Header is used to + identify the format of the Wireless-Specific Information optional + field. The HLEN field assumes 4-byte alignment, and this field + MUST be padded with zeroes (0x00) if it is not 4-byte aligned. + + The Wireless-Specific Information field uses the following format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length | Data... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Length: The 8-bit field contains the length of the data field, + with a maximum size of 255. + + Data: Wireless-specific information, defined by the wireless- + specific binding specified in the CAPWAP Header's WBID field. + + Payload: This field contains the header for a CAPWAP Data Message or + CAPWAP Control Message, followed by the data contained in the + message. + +4.4. CAPWAP Data Messages + + There are two different types of CAPWAP Data packets: CAPWAP Data + Channel Keep-Alive packets and Data Payload packets. The first is + used by the WTP to synchronize the control and data channels and to + maintain freshness of the data channel. The second is used to + transmit user payloads between the AC and WTP. This section + describes both types of CAPWAP Data packet formats. + + + + +Calhoun, et al. Standards Track [Page 50] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Both CAPWAP Data messages are transmitted on the CAPWAP Data channel. + +4.4.1. CAPWAP Data Channel Keep-Alive + + The CAPWAP Data Channel Keep-Alive packet is used to bind the CAPWAP + control channel with the data channel, and to maintain freshness of + the data channel, ensuring that the channel is still functioning. + The CAPWAP Data Channel Keep-Alive packet is transmitted by the WTP + when the DataChannelKeepAlive timer expires (see Section 4.7.2). + When the CAPWAP Data Channel Keep-Alive packet is transmitted, the + WTP sets the DataChannelDeadInterval timer. + + In the CAPWAP Data Channel Keep-Alive packet, all of the fields in + the CAPWAP Header, except the HLEN field and the 'K' bit, are set to + zero upon transmission. Upon receiving a CAPWAP Data Channel Keep- + Alive packet, the AC transmits a CAPWAP Data Channel Keep-Alive + packet back to the WTP. The contents of the transmitted packet are + identical to the contents of the received packet. + + Upon receiving a CAPWAP Data Channel Keep-Alive packet, the WTP + cancels the DataChannelDeadInterval timer and resets the + DataChannelKeepAlive timer. The CAPWAP Data Channel Keep-Alive + packet is retransmitted by the WTP in the same manner as the CAPWAP + Control messages. If the DataChannelDeadInterval timer expires, the + WTP tears down the control DTLS session, and the data DTLS session if + one existed. + + The CAPWAP Data Channel Keep-Alive packet contains the following + payload immediately following the CAPWAP Header (see Section 4.3). + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Message Element Length | Message Element [0..N] ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Message Element Length: The 16-bit Length field indicates the + number of bytes following the CAPWAP Header, with a maximum size + of 65535. + + Message Element[0..N]: The message element(s) carry the information + pertinent to each of the CAPWAP Data Channel Keep-Alive message. + The following message elements MUST be present in this CAPWAP + message: + + Session ID, see Section 4.6.37. + + + + + +Calhoun, et al. Standards Track [Page 51] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.4.2. Data Payload + + A CAPWAP protocol Data Payload packet encapsulates a forwarded + wireless frame. The CAPWAP protocol defines two different modes of + encapsulation: IEEE 802.3 and native wireless. IEEE 802.3 + encapsulation requires that for 802.11 frames, the 802.11 + *Integration* function be performed in the WTP. An IEEE 802.3- + encapsulated user payload frame has the following format: + + +------------------------------------------------------+ + | IP Header | UDP Header | CAPWAP Header | 802.3 Frame | + +------------------------------------------------------+ + + The CAPWAP protocol also defines the native wireless encapsulation + mode. The format of the encapsulated CAPWAP Data frame is subject to + the rules defined by the specific wireless technology binding. Each + wireless technology binding MUST contain a section entitled "Payload + Encapsulation", which defines the format of the wireless payload that + is encapsulated within CAPWAP Data packets. + + For 802.3 payload frames, the 802.3 frame is encapsulated (excluding + the IEEE 802.3 Preamble, Start Frame Delimiter (SFD), and Frame Check + Sequence (FCS) fields). If the encapsulated frame would exceed the + transport layer's MTU, the sender is responsible for the + fragmentation of the frame, as specified in Section 3.4. The CAPWAP + protocol can support IEEE 802.3 frames whose length is defined in the + IEEE 802.3as specification [FRAME-EXT]. + +4.4.3. Establishment of a DTLS Data Channel + + If the AC and WTP are configured to tunnel the data channel over + DTLS, the proper DTLS session must be initiated. To avoid having to + reauthenticate and reauthorize an AC and WTP, the DTLS data channel + SHOULD be initiated using the TLS session resumption feature + [RFC5246]. + + The AC DTLS implementation MUST NOT initiate a data channel session + for a DTLS session for which there is no active control channel + session. + +4.5. CAPWAP Control Messages + + The CAPWAP Control protocol provides a control channel between the + WTP and the AC. Control messages are divided into the following + message types: + + Discovery: CAPWAP Discovery messages are used to identify potential + ACs, their load and capabilities. + + + +Calhoun, et al. Standards Track [Page 52] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Join: CAPWAP Join messages are used by a WTP to request service from + an AC, and for the AC to respond to the WTP. + + Control Channel Management: CAPWAP Control channel management + messages are used to maintain the control channel. + + WTP Configuration Management: The WTP Configuration messages are + used by the AC to deliver a specific configuration to the WTP. + Messages that retrieve statistics from a WTP are also included in + WTP Configuration Management. + + Station Session Management: Station Session Management messages are + used by the AC to deliver specific station policies to the WTP. + + Device Management Operations: Device management operations are used + to request and deliver a firmware image to the WTP. + + Binding-Specific CAPWAP Management Messages: Messages in this + category are used by the AC and the WTP to exchange protocol- + specific CAPWAP management messages. These messages may or may + not be used to change the link state of a station. + + Discovery, Join, Control Channel Management, WTP Configuration + Management, and Station Session Management CAPWAP Control messages + MUST be implemented. Device Management Operations messages MAY be + implemented. + + CAPWAP Control messages sent from the WTP to the AC indicate that the + WTP is operational, providing an implicit keep-alive mechanism for + the WTP. The Control Channel Management Echo Request and Echo + Response messages provide an explicit keep-alive mechanism when other + CAPWAP Control messages are not exchanged. + +4.5.1. Control Message Format + + All CAPWAP Control messages are sent encapsulated within the CAPWAP + Header (see Section 4.3). Immediately following the CAPWAP Header is + the control header, which has the following format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Message Type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Seq Num | Msg Element Length | Flags | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Msg Element [0..N] ... + +-+-+-+-+-+-+-+-+-+-+-+-+ + + + +Calhoun, et al. Standards Track [Page 53] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.5.1.1. Message Type + + The Message Type field identifies the function of the CAPWAP Control + message. To provide extensibility, the Message Type field is + comprised of an IANA Enterprise Number [RFC3232] and an enterprise- + specific message type number. The first three octets contain the + IANA Enterprise Number in network byte order, with zero used for + CAPWAP base protocol (this specification) defined message types. The + last octet is the enterprise-specific message type number, which has + a range from 0 to 255. + + The Message Type field is defined as: + + Message Type = + IANA Enterprise Number * 256 + + Enterprise Specific Message Type Number + + The CAPWAP protocol reliability mechanism requires that messages be + defined in pairs, consisting of both a Request and a Response + message. The Response message MUST acknowledge the Request message. + The assignment of CAPWAP Control Message Type Values always occurs in + pairs. All Request messages have odd numbered Message Type Values, + and all Response messages have even numbered Message Type Values. + The Request value MUST be assigned first. As an example, assigning a + Message Type Value of 3 for a Request message and 4 for a Response + message is valid, while assigning a Message Type Value of 4 for a + Response message and 5 for the corresponding Request message is + invalid. + + When a WTP or AC receives a message with a Message Type Value field + that is not recognized and is an odd number, the number in the + Message Type Value Field is incremented by one, and a Response + message with a Message Type Value field containing the incremented + value and containing the Result Code message element with the value + (Unrecognized Request) is returned to the sender of the received + message. If the unknown message type is even, the message is + ignored. + + + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 54] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + The valid values for CAPWAP Control Message Types are specified in + the table below: + + CAPWAP Control Message Message Type + Value + Discovery Request 1 + Discovery Response 2 + Join Request 3 + Join Response 4 + Configuration Status Request 5 + Configuration Status Response 6 + Configuration Update Request 7 + Configuration Update Response 8 + WTP Event Request 9 + WTP Event Response 10 + Change State Event Request 11 + Change State Event Response 12 + Echo Request 13 + Echo Response 14 + Image Data Request 15 + Image Data Response 16 + Reset Request 17 + Reset Response 18 + Primary Discovery Request 19 + Primary Discovery Response 20 + Data Transfer Request 21 + Data Transfer Response 22 + Clear Configuration Request 23 + Clear Configuration Response 24 + Station Configuration Request 25 + Station Configuration Response 26 + +4.5.1.2. Sequence Number + + The Sequence Number field is an identifier value used to match + Request and Response packets. When a CAPWAP packet with a Request + Message Type Value is received, the value of the Sequence Number + field is copied into the corresponding Response message. + + When a CAPWAP Control message is sent, the sender's internal sequence + number counter is monotonically incremented, ensuring that no two + pending Request messages have the same sequence number. The Sequence + Number field wraps back to zero. + +4.5.1.3. Message Element Length + + The Length field indicates the number of bytes following the Sequence + Number field. + + + +Calhoun, et al. Standards Track [Page 55] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.5.1.4. Flags + + The Flags field MUST be set to zero. + +4.5.1.5. Message Element [0..N] + + The message element(s) carry the information pertinent to each of the + control message types. Every control message in this specification + specifies which message elements are permitted. + + When a WTP or AC receives a CAPWAP message without a message element + that is specified as mandatory for the CAPWAP message, then the + CAPWAP message is discarded. If the received message was a Request + message for which the corresponding Response message carries message + elements, then a corresponding Response message with a Result Code + message element indicating "Failure - Missing Mandatory Message + Element" is returned to the sender. + + When a WTP or AC receives a CAPWAP message with a message element + that the WTP or AC does not recognize, the CAPWAP message is + discarded. If the received message was a Request message for which + the corresponding Response message carries message elements, then a + corresponding Response message with a Result Code message element + indicating "Failure - Unrecognized Message Element" and one or more + Returned Message Element message elements is included, containing the + unrecognized message element(s). + +4.5.2. Quality of Service + + The CAPWAP base protocol does not provide any Quality of Service + (QoS) recommendations for use with the CAPWAP Data messages. Any + wireless-specific CAPWAP binding specification that has QoS + requirements MUST define the application of QoS to the CAPWAP Data + messages. + + The IP header also includes the Explicit Congestion Notification + (ECN) bits [RFC3168]. Section 9.1.1 of [RFC3168] describes two + levels of ECN functionality: full functionality and limited + functionality. CAPWAP ACs and WTPs SHALL implement the limited + functionality and are RECOMMENDED to implement the full functionality + described in [RFC3168]. + + + + + + + + + + +Calhoun, et al. Standards Track [Page 56] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.5.2.1. Applying QoS to CAPWAP Control Message + + It is recommended that CAPWAP Control messages be sent by both the AC + and the WTP with an appropriate Quality-of-Service precedence value, + ensuring that congestion in the network minimizes occurrences of + CAPWAP Control channel disconnects. Therefore, a QoS-enabled CAPWAP + device SHOULD use the following values: + + 802.1Q: The priority tag of 7 SHOULD be used. + + DSCP: The CS6 per-hop behavior Service Class SHOULD be used, which + is described in [RFC2474]). + +4.5.3. Retransmissions + + The CAPWAP Control protocol operates as a reliable transport. For + each Request message, a Response message is defined, which is used to + acknowledge receipt of the Request message. In addition, the control + header Sequence Number field is used to pair the Request and Response + messages (see Section 4.5.1). + + Response messages are not explicitly acknowledged; therefore, if a + Response message is not received, the original Request message is + retransmitted. + + Implementations MUST keep track of the sequence number of the last + received Request message, and MUST cache the corresponding Response + message. If a retransmission with the same sequence number is + received, the cached Response message MUST be retransmitted without + re-processing the Request. If an older Request message is received, + meaning one where the sequence number is smaller, it MUST be ignored. + A newer Request message, meaning one whose sequence number is larger, + is processed as usual. + + Note: A sequence number is considered "smaller" when s1 is smaller + than s2 modulo 256 if and only if (s1s2 and (s1-s2)>128). + + Both the WTP and the AC can only have a single request outstanding at + any given time. Retransmitted Request messages MUST NOT be altered + by the sender. + + After transmitting a Request message, the RetransmitInterval (see + Section 4.7) timer and MaxRetransmit (see Section 4.8) variable are + used to determine if the original Request message needs to be + retransmitted. The RetransmitInterval timer is used the first time + the Request is retransmitted. The timer is then doubled every + + + + +Calhoun, et al. Standards Track [Page 57] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + subsequent time the same Request message is retransmitted, up to + MaxRetransmit but no more than half the EchoInterval timer (see + Section 4.7.7). Response messages are not subject to these timers. + + If the sender stops retransmitting a Request message before reaching + MaxRetransmit retransmissions (which leads to transition to DTLS + Teardown, as described in Section 2.3.1), it cannot know whether the + recipient received and processed the Request or not. In most + situations, the sender SHOULD NOT do this, and instead continue + retransmitting until a Response message is received, or transition to + DTLS Teardown occurs. However, if the sender does decide to continue + the connection with a new or modified Request message, the new + message MUST have a new sequence number, and be treated as a new + Request message by the receiver. Note that there is a high chance + that both the WTP and the AC's sequence numbers will become out of + sync. + + When a Request message is retransmitted, it MUST be re-encrypted via + the DTLS stack. If the peer had received the Request message, and + the corresponding Response message was lost, it is necessary to + ensure that retransmitted Request messages are not identified as + replays by the DTLS stack. Similarly, any cached Response messages + that are retransmitted as a result of receiving a retransmitted + Request message MUST be re-encrypted via DTLS. + + Duplicate Response messages, identified by the Sequence Number field + in the CAPWAP Control message header, SHOULD be discarded upon + receipt. + +4.6. CAPWAP Protocol Message Elements + + This section defines the CAPWAP Protocol message elements that are + included in CAPWAP protocol control messages. + + Message elements are used to carry information needed in control + messages. Every message element is identified by the Type Value + field, defined below. The total length of the message elements is + indicated in the message element's length field. + + All of the message element definitions in this document use a diagram + similar to the one below in order to depict its format. Note that to + simplify this specification, these diagrams do not include the header + fields (Type and Length). The header field values are defined in the + message element descriptions. + + + + + + + +Calhoun, et al. Standards Track [Page 58] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Unless otherwise specified, a control message that lists a set of + supported (or expected) message elements MUST NOT expect the message + elements to be in any specific order. The sender MAY include the + message elements in any order. Unless otherwise noted, one message + element of each type is present in a given control message. + + Unless otherwise specified, any configuration information sent by the + AC to the WTP MAY be saved to non-volatile storage (see Section 8.1) + for more information). + + Additional message elements may be defined in separate IETF + documents. + + The format of a message element uses the TLV format shown here: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Value ... | + +-+-+-+-+-+-+-+-+ + + The 16-bit Type field identifies the information carried in the Value + field and Length (16 bits) indicates the number of bytes in the Value + field. The value of zero (0) is reserved and MUST NOT be used. The + rest of the Type field values are allocated as follows: + + Usage Type Values + + CAPWAP Protocol Message Elements 1 - 1023 + IEEE 802.11 Message Elements 1024 - 2047 + Reserved for Future Use 2048 - 3071 + EPCGlobal Message Elements 3072 - 4095 + Reserved for Future Use 4096 - 65535 + + The table below lists the CAPWAP protocol Message Elements and their + Type values. + + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 59] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + CAPWAP Message Element Type Value + + AC Descriptor 1 + AC IPv4 List 2 + AC IPv6 List 3 + AC Name 4 + AC Name with Priority 5 + AC Timestamp 6 + Add MAC ACL Entry 7 + Add Station 8 + Reserved 9 + CAPWAP Control IPV4 Address 10 + CAPWAP Control IPV6 Address 11 + CAPWAP Local IPV4 Address 30 + CAPWAP Local IPV6 Address 50 + CAPWAP Timers 12 + CAPWAP Transport Protocol 51 + Data Transfer Data 13 + Data Transfer Mode 14 + Decryption Error Report 15 + Decryption Error Report Period 16 + Delete MAC ACL Entry 17 + Delete Station 18 + Reserved 19 + Discovery Type 20 + Duplicate IPv4 Address 21 + Duplicate IPv6 Address 22 + ECN Support 53 + Idle Timeout 23 + Image Data 24 + Image Identifier 25 + Image Information 26 + Initiate Download 27 + Location Data 28 + Maximum Message Length 29 + MTU Discovery Padding 52 + Radio Administrative State 31 + Radio Operational State 32 + Result Code 33 + Returned Message Element 34 + Session ID 35 + Statistics Timer 36 + Vendor Specific Payload 37 + WTP Board Data 38 + WTP Descriptor 39 + WTP Fallback 40 + WTP Frame Tunnel Mode 41 + Reserved 42 + + + +Calhoun, et al. Standards Track [Page 60] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Reserved 43 + WTP MAC Type 44 + WTP Name 45 + Unused/Reserved 46 + WTP Radio Statistics 47 + WTP Reboot Statistics 48 + WTP Static IP Address Information 49 + +4.6.1. AC Descriptor + + The AC Descriptor message element is used by the AC to communicate + its current state. The value contains the following fields. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Stations | Limit | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Active WTPs | Max WTPs | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Security | R-MAC Field | Reserved1 | DTLS Policy | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | AC Information Sub-Element... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1 for AC Descriptor + + Length: >= 12 + + Stations: The number of stations currently served by the AC + + Limit: The maximum number of stations supported by the AC + + Active WTPs: The number of WTPs currently attached to the AC + + Max WTPs: The maximum number of WTPs supported by the AC + + Security: An 8-bit mask specifying the authentication credential + type supported by the AC (see Section 2.4.4). The field has the + following format: + + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |Reserved |S|X|R| + +-+-+-+-+-+-+-+-+ + + + + + + +Calhoun, et al. Standards Track [Page 61] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Reserved: A set of reserved bits for future use. All + implementations complying with this protocol MUST set to zero + any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all + bits not defined for the version of the protocol they support. + + S: The AC supports the pre-shared secret authentication, as + described in Section 12.6. + + X: The AC supports X.509 Certificate authentication, as + described in Section 12.7. + + R: A reserved bit for future use. All implementations + complying with this protocol MUST set to zero any bits that + are reserved in the version of the protocol supported by + that implementation. Receivers MUST ignore all bits not + defined for the version of the protocol they support. + + R-MAC Field: The AC supports the optional Radio MAC Address field + in the CAPWAP transport header (see Section 4.3). The following + enumerated values are supported: + + 0 - Reserved + + 1 - Supported + + 2 - Not Supported + + Reserved: A set of reserved bits for future use. All + implementations complying with this protocol MUST set to zero any + bits that are reserved in the version of the protocol supported by + that implementation. Receivers MUST ignore all bits not defined + for the version of the protocol they support. + + DTLS Policy: The AC communicates its policy on the use of DTLS for + the CAPWAP data channel. The AC MAY communicate more than one + supported option, represented by the bit field below. The WTP + MUST abide by one of the options communicated by AC. The field + has the following format: + + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |Reserved |D|C|R| + +-+-+-+-+-+-+-+-+ + + + + + + + +Calhoun, et al. Standards Track [Page 62] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Reserved: A set of reserved bits for future use. All + implementations complying with this protocol MUST set to zero + any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all + bits not defined for the version of the protocol they support. + + D: DTLS-Enabled Data Channel Supported + + C: Clear Text Data Channel Supported + + R: A reserved bit for future use. All implementations + complying with this protocol MUST set to zero any bits that + are reserved in the version of the protocol supported by + that implementation. Receivers MUST ignore all bits not + defined for the version of the protocol they support. + + AC Information Sub-Element: The AC Descriptor message element + contains multiple AC Information sub-elements, and defines two + sub-types, each of which MUST be present. The AC Information sub- + element has the following format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | AC Information Vendor Identifier | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | AC Information Type | AC Information Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | AC Information Data... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + AC Information Vendor Identifier: A 32-bit value containing the + IANA-assigned "Structure of Management Information (SMI) + Network Management Private Enterprise Codes". + + AC Information Type: Vendor-specific encoding of AC information + in the UTF-8 format [RFC3629]. The following enumerated values + are supported. Both the Hardware and Software Version sub- + elements MUST be included in the AC Descriptor message element. + The values listed below are used in conjunction with the AC + Information Vendor Identifier field, whose value MUST be set to + zero (0). This field, combined with the AC Information Vendor + Identifier set to a non-zero (0) value, allows vendors to use a + private namespace. + + + + + + + +Calhoun, et al. Standards Track [Page 63] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 4 - Hardware Version: The AC's hardware version number. + + 5 - Software Version: The AC's Software (firmware) version + number. + + AC Information Length: Length of vendor-specific encoding of AC + information, with a maximum size of 1024. + + AC Information Data: Vendor-specific encoding of AC information. + +4.6.2. AC IPv4 List + + The AC IPv4 List message element is used to configure a WTP with the + latest list of ACs available for the WTP to join. + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | AC IP Address[] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 2 for AC IPv4 List + + Length: >= 4 + + AC IP Address: An array of 32-bit integers containing AC IPv4 + Addresses, containing no more than 1024 addresses. + +4.6.3. AC IPv6 List + + The AC IPv6 List message element is used to configure a WTP with the + latest list of ACs available for the WTP to join. + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | AC IP Address[] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | AC IP Address[] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | AC IP Address[] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | AC IP Address[] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + + +Calhoun, et al. Standards Track [Page 64] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Type: 3 for AC IPV6 List + + Length: >= 16 + + AC IP Address: An array of 128-bit integers containing AC IPv6 + Addresses, containing no more than 1024 addresses. + +4.6.4. AC Name + + The AC Name message element contains an UTF-8 [RFC3629] + representation of the AC identity. The value is a variable-length + byte string. The string is NOT zero terminated. + + 0 + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + | Name ... + +-+-+-+-+-+-+-+-+ + + Type: 4 for AC Name + + Length: >= 1 + + Name: A variable-length UTF-8 encoded string [RFC3629] containing + the AC's name, whose maximum size MUST NOT exceed 512 bytes. + +4.6.5. AC Name with Priority + + The AC Name with Priority message element is sent by the AC to the + WTP to configure preferred ACs. The number of instances of this + message element is equal to the number of ACs configured on the WTP. + The WTP also uses this message element to send its configuration to + the AC. + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Priority | AC Name... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 5 for AC Name with Priority + + Length: >= 2 + + Priority: A value between 1 and 255 specifying the priority order + of the preferred AC. For instance, the value of one (1) is used + to set the primary AC, the value of two (2) is used to set the + secondary, etc. + + + +Calhoun, et al. Standards Track [Page 65] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + AC Name: A variable-length UTF-8 encoded string [RFC3629] + containing the AC name, whose maximum size MUST NOT exceed 512 + bytes. + +4.6.6. AC Timestamp + + The AC Timestamp message element is sent by the AC to synchronize the + WTP clock. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 6 for AC Timestamp + + Length: 4 + + Timestamp: The AC's current time, allowing all of the WTPs to be + time synchronized in the format defined by Network Time Protocol + (NTP) in RFC 1305 [RFC1305]. Only the most significant 32 bits of + the NTP time are included in this field. + +4.6.7. Add MAC ACL Entry + + The Add MAC Access Control List (ACL) Entry message element is used + by an AC to add a MAC ACL list entry on a WTP, ensuring that the WTP + no longer provides service to the MAC addresses provided in the + message. The MAC addresses provided in this message element are not + expected to be saved in non-volatile memory on the WTP. The MAC ACL + table on the WTP is cleared every time the WTP establishes a new + session with an AC. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Num of Entries| Length | MAC Address ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 7 for Add MAC ACL Entry + + Length: >= 8 + + Num of Entries: The number of instances of the Length/MAC Address + fields in the array. This value MUST NOT exceed 255. + + + + + +Calhoun, et al. Standards Track [Page 66] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Length: The length of the MAC Address field. The formats and + lengths specified in [EUI-48] and [EUI-64] are supported. + + MAC Address: MAC addresses to add to the ACL. + +4.6.8. Add Station + + The Add Station message element is used by the AC to inform a WTP + that it should forward traffic for a station. The Add Station + message element is accompanied by technology-specific binding + information element(s), which may include security parameters. + Consequently, the security parameters MUST be applied by the WTP for + the station. + + After station policy has been delivered to the WTP through the Add + Station message element, an AC MAY change any policies by sending a + modified Add Station message element. When a WTP receives an Add + Station message element for an existing station, it MUST override any + existing state for the station. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Length | MAC Address ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | VLAN Name... + +-+-+-+-+-+-+-+-+ + + Type: 8 for Add Station + + Length: >= 8 + + Radio ID: An 8-bit value representing the radio, whose value is + between one (1) and 31. + + Length: The length of the MAC Address field. The formats and + lengths specified in [EUI-48] and [EUI-64] are supported. + + MAC Address: The station's MAC address. + + VLAN Name: An optional variable-length UTF-8 encoded string + [RFC3629], with a maximum length of 512 octets, containing the + VLAN Name on which the WTP is to locally bridge user data. Note + this field is only valid with WTPs configured in Local MAC mode. + + + + + + + +Calhoun, et al. Standards Track [Page 67] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.6.9. CAPWAP Control IPv4 Address + + The CAPWAP Control IPv4 Address message element is sent by the AC to + the WTP during the Discovery process and is used by the AC to provide + the interfaces available on the AC, and the current number of WTPs + connected. When multiple CAPWAP Control IPV4 Address message + elements are returned, the WTP SHOULD perform load balancing across + the multiple interfaces (see Section 6.1). + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | WTP Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 10 for CAPWAP Control IPv4 Address + + Length: 6 + + IP Address: The IP address of an interface. + + WTP Count: The number of WTPs currently connected to the interface, + with a maximum value of 65535. + +4.6.10. CAPWAP Control IPv6 Address + + The CAPWAP Control IPv6 Address message element is sent by the AC to + the WTP during the Discovery process and is used by the AC to provide + the interfaces available on the AC, and the current number of WTPs + connected. This message element is useful for the WTP to perform + load balancing across multiple interfaces (see Section 6.1). + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | WTP Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + +Calhoun, et al. Standards Track [Page 68] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Type: 11 for CAPWAP Control IPv6 Address + + Length: 18 + + IP Address: The IP address of an interface. + + WTP Count: The number of WTPs currently connected to the interface, + with a maximum value of 65535. + +4.6.11. CAPWAP Local IPv4 Address + + The CAPWAP Local IPv4 Address message element is sent by either the + WTP, in the Join Request, or by the AC, in the Join Response. The + CAPWAP Local IPv4 Address message element is used to communicate the + IP Address of the transmitter. The receiver uses this to determine + whether a middlebox exists between the two peers, by comparing the + source IP address of the packet against the value of the message + element. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 30 for CAPWAP Local IPv4 Address + + Length: 4 + + IP Address: The IP address of the sender. + +4.6.12. CAPWAP Local IPv6 Address + + The CAPWAP Local IPv6 Address message element is sent by either the + WTP, in the Join Request, or by the AC, in the Join Response. The + CAPWAP Local IPv6 Address message element is used to communicate the + IP Address of the transmitter. The receiver uses this to determine + whether a middlebox exists between the two peers, by comparing the + source IP address of the packet against the value of the message + element. + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 69] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 50 for CAPWAP Local IPv6 Address + + Length: 16 + + IP Address: The IP address of the sender. + +4.6.13. CAPWAP Timers + + The CAPWAP Timers message element is used by an AC to configure + CAPWAP timers on a WTP. + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Discovery | Echo Request | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 12 for CAPWAP Timers + + Length: 2 + + Discovery: The number of seconds between CAPWAP Discovery messages, + when the WTP is in the Discovery phase. This value is used to + configure the MaxDiscoveryInterval timer (see Section 4.7.10). + + Echo Request: The number of seconds between WTP Echo Request CAPWAP + messages. This value is used to configure the EchoInterval timer + (see Section 4.7.7). The AC sets its EchoInterval timer to this + value, plus the maximum retransmission time as described in + Section 4.5.3. + + + + + + + + + +Calhoun, et al. Standards Track [Page 70] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.6.14. CAPWAP Transport Protocol + + When CAPWAP is run over IPv6, the UDP-Lite or UDP transports MAY be + used (see Section 3). The CAPWAP IPv6 Transport Protocol message + element is used by either the WTP or the AC to signal which transport + protocol is to be used for the CAPWAP data channel. + + Upon receiving the Join Request, the AC MAY set the CAPWAP Transport + Protocol to UDP-Lite in the Join Response message if the CAPWAP + message was received over IPv6, and the CAPWAP Local IPv6 Address + message element (see Section 4.6.12) is present and no middlebox was + detected (see Section 11). + + Upon receiving the Join Response, the WTP MAY set the CAPWAP + Transport Protocol to UDP-Lite in the Configuration Status Request or + Image Data Request message if the AC advertised support for UDP-Lite, + the message was received over IPv6, the CAPWAP Local IPv6 Address + message element (see Section 4.6.12) and no middlebox was detected + (see Section 11). Upon receiving either the Configuration Status + Request or the Image Data Request, the AC MUST observe the preference + indicated by the WTP in the CAPWAP Transport Protocol, as long as it + is consistent with what the AC advertised in the Join Response. + + For any other condition, the CAPWAP Transport Protocol MUST be set to + UDP. + + 0 + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + | Transport | + +-+-+-+-+-+-+-+-+ + + Type: 51 for CAPWAP Transport Protocol + + Length: 1 + + Transport: The transport to use for the CAPWAP Data channel. The + following enumerated values are supported: + + 1 - UDP-Lite: The UDP-Lite transport protocol is to be used for + the CAPWAP Data channel. Note that this option MUST NOT be + used if the CAPWAP Control channel is being used over IPv4. + + 2 - UDP: The UDP transport protocol is to be used for the CAPWAP + Data channel. + + + + + + +Calhoun, et al. Standards Track [Page 71] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.6.15. Data Transfer Data + + The Data Transfer Data message element is used by the WTP to provide + information to the AC for debugging purposes. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data Type | Data Mode | Data Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data .... + +-+-+-+-+-+-+-+-+ + + Type: 13 for Data Transfer Data + + Length: >= 5 + + Data Type: An 8-bit value representing the transfer Data Type. The + following enumerated values are supported: + + 1 - Transfer data is included. + + 2 - Last Transfer Data Block is included (End of File (EOF)). + + 5 - An error occurred. Transfer is aborted. + + Data Mode: An 8-bit value describing the type of information being + transmitted. The following enumerated values are supported: + + 0 - Reserved + + 1 - WTP Crash Data + + 2 - WTP Memory Dump + + Data Length: Length of data field, with a maximum size of 65535. + + Data: Data being transferred from the WTP to the AC, whose type is + identified via the Data Mode field. + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 72] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.6.16. Data Transfer Mode + + The Data Transfer Mode message element is used by the WTP to indicate + the type of data transfer information it is sending to the AC for + debugging purposes. + + 0 + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + | Data Mode | + +-+-+-+-+-+-+-+-+ + + Type: 14 for Data Transfer Mode + + Length: 1 + + Data Mode: An 8-bit value describing the type of information being + requested. The following enumerated values are supported: + + 0 - Reserved + + 1 - WTP Crash Data + + 2 - WTP Memory Dump + +4.6.17. Decryption Error Report + + The Decryption Error Report message element value is used by the WTP + to inform the AC of decryption errors that have occurred since the + last report. Note that this error reporting mechanism is not used if + encryption and decryption services are provided in the AC. + + 0 1 2 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID |Num Of Entries | Length | MAC Address... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 15 for Decryption Error Report + + Length: >= 9 + + Radio ID: The Radio Identifier refers to an interface index on the + WTP, whose value is between one (1) and 31. + + Num of Entries: The number of instances of the Length/MAC Address + fields in the array. This field MUST NOT exceed the value of 255. + + + + +Calhoun, et al. Standards Track [Page 73] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Length: The length of the MAC Address field. The formats and + lengths specified in [EUI-48] and [EUI-64] are supported. + + MAC Address: MAC address of the station that has caused decryption + errors. + +4.6.18. Decryption Error Report Period + + The Decryption Error Report Period message element value is used by + the AC to inform the WTP how frequently it should send decryption + error report messages. Note that this error reporting mechanism is + not used if encryption and decryption services are provided in the + AC. + + 0 1 2 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Report Interval | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 16 for Decryption Error Report Period + + Length: 3 + + Radio ID: The Radio Identifier refers to an interface index on the + WTP, whose value is between one (1) and 31. + + Report Interval: A 16-bit unsigned integer indicating the time, in + seconds. The default value for this message element can be found + in Section 4.7.11. + +4.6.19. Delete MAC ACL Entry + + The Delete MAC ACL Entry message element is used by an AC to delete a + MAC ACL entry on a WTP, ensuring that the WTP provides service to the + MAC addresses provided in the message. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Num of Entries| Length | MAC Address ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 17 for Delete MAC ACL Entry + + Length: >= 8 + + + + + +Calhoun, et al. Standards Track [Page 74] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Num of Entries: The number of instances of the Length/MAC Address + fields in the array. This field MUST NOT exceed the value of 255. + + Length: The length of the MAC Address field. The formats and + lengths specified in [EUI-48] and [EUI-64] are supported. + + MAC Address: An array of MAC addresses to delete from the ACL. + +4.6.20. Delete Station + + The Delete Station message element is used by the AC to inform a WTP + that it should no longer provide service to a particular station. + The WTP MUST terminate service to the station immediately upon + receiving this message element. + + The transmission of a Delete Station message element could occur for + various reasons, including for administrative reasons, or if the + station has roamed to another WTP. + + The Delete Station message element MAY be sent by the WTP, in the WTP + Event Request message, to inform the AC that a particular station is + no longer being provided service. This could occur as a result of an + Idle Timeout (see section 4.4.43), due to internal resource shortages + or for some other reason. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Length | MAC Address... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 18 for Delete Station + + Length: >= 8 + + Radio ID: An 8-bit value representing the radio, whose value is + between one (1) and 31. + + Length: The length of the MAC Address field. The formats and + lengths specified in [EUI-48] and [EUI-64] are supported. + + MAC Address: The station's MAC address. + +4.6.21. Discovery Type + + The Discovery Type message element is used by the WTP to indicate how + it has come to know about the existence of the AC to which it is + sending the Discovery Request message. + + + +Calhoun, et al. Standards Track [Page 75] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 0 + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + | Discovery Type| + +-+-+-+-+-+-+-+-+ + + Type: 20 for Discovery Type + + Length: 1 + + Discovery Type: An 8-bit value indicating how the WTP discovered + the AC. The following enumerated values are supported: + + 0 - Unknown + + 1 - Static Configuration + + 2 - DHCP + + 3 - DNS + + 4 - AC Referral (used when the AC was configured either through + the AC IPv4 List or AC IPv6 List message element) + +4.6.22. Duplicate IPv4 Address + + The Duplicate IPv4 Address message element is used by a WTP to inform + an AC that it has detected another IP device using the same IP + address that the WTP is currently using. + + The WTP MUST transmit this message element with the status set to 1 + after it has detected a duplicate IP address. When the WTP detects + that the duplicate IP address has been cleared, it MUST send this + message element with the status set to 0. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Status | Length | MAC Address ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 21 for Duplicate IPv4 Address + + Length: >= 12 + + IP Address: The IP address currently used by the WTP. + + + +Calhoun, et al. Standards Track [Page 76] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Status: The status of the duplicate IP address. The value MUST be + set to 1 when a duplicate address is detected, and 0 when the + duplicate address has been cleared. + + Length: The length of the MAC Address field. The formats and + lengths specified in [EUI-48] and [EUI-64] are supported. + + MAC Address: The MAC address of the offending device. + +4.6.23. Duplicate IPv6 Address + + The Duplicate IPv6 Address message element is used by a WTP to inform + an AC that it has detected another host using the same IP address + that the WTP is currently using. + + The WTP MUST transmit this message element with the status set to 1 + after it has detected a duplicate IP address. When the WTP detects + that the duplicate IP address has been cleared, it MUST send this + message element with the status set to 0. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Status | Length | MAC Address ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 22 for Duplicate IPv6 Address + + Length: >= 24 + + IP Address: The IP address currently used by the WTP. + + Status: The status of the duplicate IP address. The value MUST be + set to 1 when a duplicate address is detected, and 0 when the + duplicate address has been cleared. + + Length: The length of the MAC Address field. The formats and + lengths specified in [EUI-48] and [EUI-64] are supported. + + MAC Address: The MAC address of the offending device. + + + +Calhoun, et al. Standards Track [Page 77] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.6.24. Idle Timeout + + The Idle Timeout message element is sent by the AC to the WTP to + provide the Idle Timeout value that the WTP SHOULD enforce for its + active stations. The value applies to all radios on the WTP. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Timeout | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 23 for Idle Timeout + + Length: 4 + + Timeout: The current Idle Timeout, in seconds, to be enforced by + the WTP. The default value for this message element is specified + in Section 4.7.8. + +4.6.25. ECN Support + + The ECN Support message element is sent by both the WTP and the AC to + indicate their support for the Explicit Congestion Notification (ECN) + bits, as defined in [RFC3168]. + + 0 + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + | ECN Support | + +-+-+-+-+-+-+-+-+ + + Type: 53 for ECN Support + + Length: 1 + + ECN Support: An 8-bit value representing the sender's support for + ECN, as defined in [RFC3168]. All CAPWAP Implementations MUST + support the Limited ECN Support mode. Full ECN Support is used if + both the WTP and AC advertise the capability for "Full and Limited + ECN" Support; otherwise, Limited ECN Support is used. + + 0 - Limited ECN Support + + 1 - Full and Limited ECN Support + + + + + + +Calhoun, et al. Standards Track [Page 78] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.6.26. Image Data + + The Image Data message element is present in the Image Data Request + message sent by the AC and contains the following fields. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data Type | Data .... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 24 for Image Data + + Length: >= 1 + + Data Type: An 8-bit value representing the image Data Type. The + following enumerated values are supported: + + 1 - Image data is included. + + 2 - Last Image Data Block is included (EOF). + + 5 - An error occurred. Transfer is aborted. + + Data: The Image Data field contains up to 1024 characters, and its + length is inferred from this message element's length field. If + the block being sent is the last one, the Data Type field is set + to 2. The AC MAY opt to abort the data transfer by setting the + Data Type field to 5. When the Data Type field is 5, the Value + field has a zero length. + +4.6.27. Image Identifier + + The Image Identifier message element is sent by the AC to the WTP to + indicate the expected active software version that is to be run on + the WTP. The WTP sends the Image Identifier message element in order + to request a specific software version from the AC. The actual + download process is defined in Section 9.1. The value is a variable- + length UTF-8 encoded string [RFC3629], which is NOT zero terminated. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Vendor Identifier | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Data... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + +Calhoun, et al. Standards Track [Page 79] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Type: 25 for Image Identifier + + Length: >= 5 + + Vendor Identifier: A 32-bit value containing the IANA-assigned "SMI + Network Management Private Enterprise Codes". + + Data: A variable-length UTF-8 encoded string [RFC3629] containing + the firmware identifier to be run on the WTP, whose length MUST + NOT exceed 1024 octets. The length of this field is inferred from + this message element's length field. + +4.6.28. Image Information + + The Image Information message element is present in the Image Data + Response message sent by the AC to the WTP and contains the following + fields. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | File Size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Hash | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Hash | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Hash | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Hash | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 26 for Image Information + + Length: 20 + + File Size: A 32-bit value containing the size of the file, in + bytes, that will be transferred by the AC to the WTP. + + Hash: A 16-octet MD5 hash of the image using the procedures defined + in [RFC1321]. + + + + + + + + + + +Calhoun, et al. Standards Track [Page 80] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.6.29. Initiate Download + + The Initiate Download message element is used by the WTP to inform + the AC that the AC SHOULD initiate a firmware upgrade. The AC + subsequently transmits an Image Data Request message, which includes + the Image Data message element. This message element does not + contain any data. + + Type: 27 for Initiate Download + + Length: 0 + +4.6.30. Location Data + + The Location Data message element is a variable-length byte UTF-8 + encoded string [RFC3629] containing user-defined location information + (e.g., "Next to Fridge"). This information is configurable by the + network administrator, and allows the WTP location to be determined. + The string is not zero terminated. + + 0 + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+- + | Location ... + +-+-+-+-+-+-+-+-+- + + Type: 28 for Location Data + + Length: >= 1 + + Location: A non-zero-terminated UTF-8 encoded string [RFC3629] + containing the WTP location, whose maximum size MUST NOT exceed + 1024. + +4.6.31. Maximum Message Length + + The Maximum Message Length message element is included in the Join + Request message by the WTP to indicate the maximum CAPWAP message + length that it supports to the AC. The Maximum Message Length + message element is optionally included in Join Response message by + the AC to indicate the maximum CAPWAP message length that it supports + to the WTP. + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Maximum Message Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + +Calhoun, et al. Standards Track [Page 81] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Type: 29 for Maximum Message Length + + Length: 2 + + Maximum Message Length A 16-bit unsigned integer indicating the + maximum message length. + +4.6.32. MTU Discovery Padding + + The MTU Discovery Padding message element is used as padding to + perform MTU discovery, and MUST contain octets of value 0xFF, of any + length. + + 0 + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + | Padding... + +-+-+-+-+-+-+-+- + + + Type: 52 for MTU Discovery Padding + + Length: Variable + + Pad: A variable-length pad, filled with the value 0xFF. + +4.6.33. Radio Administrative State + + The Radio Administrative State message element is used to communicate + the state of a particular radio. The Radio Administrative State + message element is sent by the AC to change the state of the WTP. + The WTP saves the value, to ensure that it remains across WTP resets. + The WTP communicates this message element during the configuration + phase, in the Configuration Status Request message, to ensure that + the AC has the WTP radio current administrative state settings. The + message element contains the following fields: + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Admin State | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 31 for Radio Administrative State + + Length: 2 + + + + + +Calhoun, et al. Standards Track [Page 82] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Radio ID: An 8-bit value representing the radio to configure, whose + value is between one (1) and 31. The Radio ID field MAY also + include the value of 0xff, which is used to identify the WTP. If + an AC wishes to change the administrative state of a WTP, it + includes 0xff in the Radio ID field. + + Admin State: An 8-bit value representing the administrative state + of the radio. The default value for the Admin State field is + listed in Section 4.8.1. The following enumerated values are + supported: + + 0 - Reserved + + 1 - Enabled + + 2 - Disabled + +4.6.34. Radio Operational State + + The Radio Operational State message element is sent by the WTP to the + AC to communicate a radio's operational state. This message element + is included in the Configuration Update Response message by the WTP + if it was requested to change the state of its radio, via the Radio + Administrative State message element, but was unable to comply to the + request. This message element is included in the Change State Event + message when a WTP radio state was changed unexpectedly. This could + occur due to a hardware failure. Note that the operational state + setting is not saved on the WTP, and therefore does not remain across + WTP resets. The value contains three fields, as shown below. + + 0 1 2 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | State | Cause | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 32 for Radio Operational State + + Length: 3 + + Radio ID: The Radio Identifier refers to an interface index on the + WTP, whose value is between one (1) and 31. A value of 0xFF is + invalid, as it is not possible to change the WTP's operational + state. + + State: An 8-bit Boolean value representing the state of the radio. + The following enumerated values are supported: + + + + +Calhoun, et al. Standards Track [Page 83] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 0 - Reserved + + 1 - Enabled + + 2 - Disabled + + Cause: When a radio is inoperable, the cause field contains the + reason the radio is out of service. The following enumerated + values are supported: + + 0 - Normal + + 1 - Radio Failure + + 2 - Software Failure + + 3 - Administratively Set + +4.6.35. Result Code + + The Result Code message element value is a 32-bit integer value, + indicating the result of the Request message corresponding to the + sequence number included in the Response message. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Result Code | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 33 for Result Code + + Length: 4 + + Result Code: The following enumerated values are defined: + + 0 Success + + 1 Failure (AC List Message Element MUST Be Present) + + 2 Success (NAT Detected) + + 3 Join Failure (Unspecified) + + 4 Join Failure (Resource Depletion) + + 5 Join Failure (Unknown Source) + + + + +Calhoun, et al. Standards Track [Page 84] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 6 Join Failure (Incorrect Data) + + 7 Join Failure (Session ID Already in Use) + + 8 Join Failure (WTP Hardware Not Supported) + + 9 Join Failure (Binding Not Supported) + + 10 Reset Failure (Unable to Reset) + + 11 Reset Failure (Firmware Write Error) + + 12 Configuration Failure (Unable to Apply Requested Configuration + - Service Provided Anyhow) + + 13 Configuration Failure (Unable to Apply Requested Configuration + - Service Not Provided) + + 14 Image Data Error (Invalid Checksum) + + 15 Image Data Error (Invalid Data Length) + + 16 Image Data Error (Other Error) + + 17 Image Data Error (Image Already Present) + + 18 Message Unexpected (Invalid in Current State) + + 19 Message Unexpected (Unrecognized Request) + + 20 Failure - Missing Mandatory Message Element + + 21 Failure - Unrecognized Message Element + + 22 Data Transfer Error (No Information to Transfer) + +4.6.36. Returned Message Element + + The Returned Message Element is sent by the WTP in the Change State + Event Request message to communicate to the AC which message elements + in the Configuration Status Response it was unable to apply locally. + The Returned Message Element message element contains a result code + indicating the reason that the configuration could not be applied, + and encapsulates the failed message element. + + + + + + + +Calhoun, et al. Standards Track [Page 85] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reason | Length | Message Element... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 34 for Returned Message Element + + Length: >= 6 + + Reason: The reason the configuration in the offending message + element could not be applied by the WTP. The following enumerated + values are supported: + + 0 - Reserved + + 1 - Unknown Message Element + + 2 - Unsupported Message Element + + 3 - Unknown Message Element Value + + 4 - Unsupported Message Element Value + + Length: The length of the Message Element field, which MUST NOT + exceed 255 octets. + + Message Element: The Message Element field encapsulates the message + element sent by the AC in the Configuration Status Response + message that caused the error. + +4.6.37. Session ID + + The Session ID message element value contains a randomly generated + unsigned 128-bit integer. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Session ID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Session ID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Session ID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Session ID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + +Calhoun, et al. Standards Track [Page 86] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Type: 35 for Session ID + + Length: 16 + + Session ID: A 128-bit unsigned integer used as a random session + identifier + +4.6.38. Statistics Timer + + The Statistics Timer message element value is used by the AC to + inform the WTP of the frequency with which it expects to receive + updated statistics. + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Statistics Timer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 36 for Statistics Timer + + Length: 2 + + Statistics Timer: A 16-bit unsigned integer indicating the time, in + seconds. The default value for this timer is specified in + Section 4.7.14. + +4.6.39. Vendor Specific Payload + + The Vendor Specific Payload message element is used to communicate + vendor-specific information between the WTP and the AC. The Vendor + Specific Payload message element MAY be present in any CAPWAP + message. The exchange of vendor-specific data between the MUST NOT + modify the behavior of the base CAPWAP protocol and state machine. + The message element uses the following format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Vendor Identifier | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Element ID | Data... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 37 for Vendor Specific Payload + + Length: >= 7 + + + + +Calhoun, et al. Standards Track [Page 87] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Vendor Identifier: A 32-bit value containing the IANA-assigned "SMI + Network Management Private Enterprise Codes" [RFC3232]. + + Element ID: A 16-bit Element Identifier that is managed by the + vendor. + + Data: Variable-length vendor-specific information, whose contents + and format are proprietary and understood based on the Element ID + field. This field MUST NOT exceed 2048 octets. + +4.6.40. WTP Board Data + + The WTP Board Data message element is sent by the WTP to the AC and + contains information about the hardware present. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Vendor Identifier | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Board Data Sub-Element... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 38 for WTP Board Data + + Length: >=14 + + Vendor Identifier: A 32-bit value containing the IANA-assigned "SMI + Network Management Private Enterprise Codes", identifying the WTP + hardware manufacturer. The Vendor Identifier field MUST NOT be + set to zero. + + Board Data Sub-Element: The WTP Board Data message element contains + multiple Board Data sub-elements, some of which are mandatory and + some are optional, as described below. The Board Data Type values + are not extensible by vendors, and are therefore not coupled along + with the Vendor Identifier field. The Board Data sub-element has + the following format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Board Data Type | Board Data Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Board Data Value... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + + +Calhoun, et al. Standards Track [Page 88] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Board Data Type: The Board Data Type field identifies the data + being encoded. The CAPWAP protocol defines the following + values, and each of these types identify whether their presence + is mandatory or optional: + + 0 - WTP Model Number: The WTP Model Number MUST be included in + the WTP Board Data message element. + + 1 - WTP Serial Number: The WTP Serial Number MUST be included in + the WTP Board Data message element. + + 2 - Board ID: A hardware identifier, which MAY be included in + the WTP Board Data message element. + + 3 - Board Revision: A revision number of the board, which MAY be + included in the WTP Board Data message element. + + 4 - Base MAC Address: The WTP's Base MAC address, which MAY be + assigned to the primary Ethernet interface. + + Board Data Length: The length of the data in the Board Data Value + field, whose length MUST NOT exceed 1024 octets. + + Board Data Value: The data associated with the Board Data Type + field for this Board Data sub-element. + +4.6.41. WTP Descriptor + + The WTP Descriptor message element is used by a WTP to communicate + its current hardware and software (firmware) configuration. The + value contains the following fields: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Max Radios | Radios in use | Num Encrypt |Encryp Sub-Elmt| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Encryption Sub-Element | Descriptor Sub-Element... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 39 for WTP Descriptor + + Length: >= 33 + + + + + + + + +Calhoun, et al. Standards Track [Page 89] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Max Radios: An 8-bit value representing the number of radios (where + each radio is identified via the Radio ID field) supported by the + WTP. + + Radios in use: An 8-bit value representing the number of radios in + use in the WTP. + + Num Encrypt: The number of 3-byte Encryption sub-elements that + follow this field. The value of the Num Encrypt field MUST be + between one (1) and 255. + + Encryption Sub-Element: The WTP Descriptor message element MUST + contain at least one Encryption sub-element. One sub-element is + present for each binding supported by the WTP. The Encryption + sub-element has the following format: + + 0 1 2 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Resvd| WBID | Encryption Capabilities | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Resvd: The 3-bit field is reserved for future use. All + implementations complying with this protocol MUST set to zero + any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all + bits not defined for the version of the protocol they support. + + WBID: A 5-bit field that is the wireless binding identifier. + The identifier will indicate the type of wireless packet + associated with the radio. The WBIDs defined in this + specification can be found in Section 4.3. + + Encryption Capabilities: This 16-bit field is used by the WTP to + communicate its capabilities to the AC. A WTP that does not + have any encryption capabilities sets this field to zero (0). + Refer to the specific wireless binding for further + specification of the Encryption Capabilities field. + + Descriptor Sub-Element: The WTP Descriptor message element contains + multiple Descriptor sub-elements, some of which are mandatory and + some are optional, as described below. The Descriptor sub-element + has the following format: + + + + + + + + +Calhoun, et al. Standards Track [Page 90] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Descriptor Vendor Identifier | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Descriptor Type | Descriptor Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Descriptor Data... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Descriptor Vendor Identifier: A 32-bit value containing the + IANA-assigned "SMI Network Management Private Enterprise + Codes". + + Descriptor Type: The Descriptor Type field identifies the data + being encoded. The format of the data is vendor-specific + encoded in the UTF-8 format [RFC3629]. The CAPWAP protocol + defines the following values, and each of these types identify + whether their presence is mandatory or optional. The values + listed below are used in conjunction with the Descriptor Vendor + Identifier field, whose value MUST be set to zero (0). This + field, combined with the Descriptor Vendor Identifier set to a + non-zero (0) value, allows vendors to use a private namespace. + + 0 - Hardware Version: The WTP hardware version number MUST be + present. + + 1 - Active Software Version: The WTP running software version + number MUST be present. + + 2 - Boot Version: The WTP boot loader version number MUST be + present. + + 3 - Other Software Version: The WTP non-running software + (firmware) version number MAY be present. This type is + used to communicate alternate software versions that are + available on the WTP's non-volatile storage. + + Descriptor Length: Length of the vendor-specific encoding of the + Descriptor Data field, whose length MUST NOT exceed 1024 + octets. + + Descriptor Data: Vendor-specific data of WTP information encoded + in the UTF-8 format [RFC3629]. + + + + + + + +Calhoun, et al. Standards Track [Page 91] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.6.42. WTP Fallback + + The WTP Fallback message element is sent by the AC to the WTP to + enable or disable automatic CAPWAP fallback in the event that a WTP + detects its preferred AC to which it is not currently connected. + + 0 + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + | Mode | + +-+-+-+-+-+-+-+-+ + + Type: 40 for WTP Fallback + + Length: 1 + + Mode: The 8-bit value indicates the status of automatic CAPWAP + fallback on the WTP. When enabled, if the WTP detects that its + primary AC is available, and that the WTP is not connected to the + primary AC, the WTP SHOULD automatically disconnect from its + current AC and reconnect to its primary AC. If disabled, the WTP + will only reconnect to its primary AC through manual intervention + (e.g., through the Reset Request message). The default value for + this field is specified in Section 4.8.9. The following + enumerated values are supported: + + 0 - Reserved + + 1 - Enabled + + 2 - Disabled + +4.6.43. WTP Frame Tunnel Mode + + The WTP Frame Tunnel Mode message element allows the WTP to + communicate the tunneling modes of operation that it supports to the + AC. A WTP that advertises support for all types allows the AC to + select which type will be used, based on its local policy. + + 0 + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |Reservd|N|E|L|U| + +-+-+-+-+-+-+-+-+ + + + + + + + +Calhoun, et al. Standards Track [Page 92] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Type: 41 for WTP Frame Tunnel Mode + + Length: 1 + + Reservd: A set of reserved bits for future use. All + implementations complying with this protocol MUST set to zero any + bits that are reserved in the version of the protocol supported by + that implementation. Receivers MUST ignore all bits not defined + for the version of the protocol they support. + + N: Native Frame Tunnel mode requires the WTP and AC to encapsulate + all user payloads as native wireless frames, as defined by the + wireless binding (see for example Section 4.4) + + E: The 802.3 Frame Tunnel Mode requires the WTP and AC to + encapsulate all user payload as native IEEE 802.3 frames (see + Section 4.4). All user traffic is tunneled to the AC. This + value MUST NOT be used when the WTP MAC Type is set to Split + MAC. + + L: When Local Bridging is used, the WTP does not tunnel user + traffic to the AC; all user traffic is locally bridged. This + value MUST NOT be used when the WTP MAC Type is set to Split + MAC. + + R: A reserved bit for future use. All implementations complying + with this protocol MUST set to zero any bits that are reserved + in the version of the protocol supported by that + implementation. Receivers MUST ignore all bits not defined for + the version of the protocol they support. + +4.6.44. WTP MAC Type + + The WTP MAC-Type message element allows the WTP to communicate its + mode of operation to the AC. A WTP that advertises support for both + modes allows the AC to select the mode to use, based on local policy. + + 0 + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + | MAC Type | + +-+-+-+-+-+-+-+-+ + + Type: 44 for WTP MAC Type + + + + + + + +Calhoun, et al. Standards Track [Page 93] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Length: 1 + + MAC Type: The MAC mode of operation supported by the WTP. The + following enumerated values are supported: + + 0 - Local MAC: Local MAC is the default mode that MUST be + supported by all WTPs. When tunneling is enabled (see + Section 4.6.43), the encapsulated frames MUST be in the + 802.3 format (see Section 4.4.2), unless a wireless + management or control frame which MAY be in its native + format. Any CAPWAP binding needs to specify the format of + management and control wireless frames. + + 1 - Split MAC: Split MAC support is optional, and allows the AC + to receive and process native wireless frames. + + 2 - Both: WTP is capable of supporting both Local MAC and Split + MAC. + +4.6.45. WTP Name + + The WTP Name message element is a variable-length byte UTF-8 encoded + string [RFC3629]. The string is not zero terminated. + + 0 + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+- + | WTP Name ... + +-+-+-+-+-+-+-+-+- + + Type: 45 for WTP Name + + Length: >= 1 + + WTP Name: A non-zero-terminated UTF-8 encoded string [RFC3629] + containing the WTP name, whose maximum size MUST NOT exceed 512 + bytes. + +4.6.46. WTP Radio Statistics + + The WTP Radio Statistics message element is sent by the WTP to the AC + to communicate statistics on radio behavior and reasons why the WTP + radio has been reset. These counters are never reset on the WTP, and + will therefore roll over to zero when the maximum size has been + reached. + + + + + + +Calhoun, et al. Standards Track [Page 94] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Last Fail Type| Reset Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SW Failure Count | HW Failure Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Other Failure Count | Unknown Failure Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Config Update Count | Channel Change Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Band Change Count | Current Noise Floor | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 47 for WTP Radio Statistics + + Length: 20 + + Radio ID: The radio ID of the radio to which the statistics apply, + whose value is between one (1) and 31. + + Last Failure Type: The last WTP failure. The following enumerated + values are supported: + + 0 - Statistic Not Supported + + 1 - Software Failure + + 2 - Hardware Failure + + 3 - Other Failure + + 255 - Unknown (e.g., WTP doesn't keep track of info) + + Reset Count: The number of times that the radio has been reset. + + SW Failure Count: The number of times that the radio has failed due + to software-related reasons. + + HW Failure Count: The number of times that the radio has failed due + to hardware-related reasons. + + Other Failure Count: The number of times that the radio has failed + due to known reasons, other than software or hardware failure. + + + + + + + +Calhoun, et al. Standards Track [Page 95] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Unknown Failure Count: The number of times that the radio has + failed for unknown reasons. + + Config Update Count: The number of times that the radio + configuration has been updated. + + Channel Change Count: The number of times that the radio channel + has been changed. + + Band Change Count: The number of times that the radio has changed + frequency bands. + + Current Noise Floor: A signed integer that indicates the noise + floor of the radio receiver in units of dBm. + +4.6.47. WTP Reboot Statistics + + The WTP Reboot Statistics message element is sent by the WTP to the + AC to communicate reasons why WTP reboots have occurred. These + counters are never reset on the WTP, and will therefore roll over to + zero when the maximum size has been reached. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reboot Count | AC Initiated Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Link Failure Count | SW Failure Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | HW Failure Count | Other Failure Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unknown Failure Count |Last Failure Type| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 48 for WTP Reboot Statistics + + Length: 15 + + Reboot Count: The number of reboots that have occurred due to a WTP + crash. A value of 65535 implies that this information is not + available on the WTP. + + AC Initiated Count: The number of reboots that have occurred at the + request of a CAPWAP protocol message, such as a change in + configuration that required a reboot or an explicit CAPWAP + protocol reset request. A value of 65535 implies that this + information is not available on the WTP. + + + + +Calhoun, et al. Standards Track [Page 96] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Link Failure Count: The number of times that a CAPWAP protocol + connection with an AC has failed due to link failure. + + SW Failure Count: The number of times that a CAPWAP protocol + connection with an AC has failed due to software-related reasons. + + HW Failure Count: The number of times that a CAPWAP protocol + connection with an AC has failed due to hardware-related reasons. + + Other Failure Count: The number of times that a CAPWAP protocol + connection with an AC has failed due to known reasons, other than + AC initiated, link, SW or HW failure. + + Unknown Failure Count: The number of times that a CAPWAP protocol + connection with an AC has failed for unknown reasons. + + Last Failure Type: The failure type of the most recent WTP failure. + The following enumerated values are supported: + + 0 - Not Supported + + 1 - AC Initiated (see Section 9.2) + + 2 - Link Failure + + 3 - Software Failure + + 4 - Hardware Failure + + 5 - Other Failure + + 255 - Unknown (e.g., WTP doesn't keep track of info) + +4.6.48. WTP Static IP Address Information + + The WTP Static IP Address Information message element is used by an + AC to configure or clear a previously configured static IP address on + a WTP. IPv6 WTPs are expected to use dynamic addresses. + + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 97] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | IP Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Netmask | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Gateway | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Static | + +-+-+-+-+-+-+-+-+ + + Type: 49 for WTP Static IP Address Information + + Length: 13 + + IP Address: The IP address to assign to the WTP. This field is + only valid if the static field is set to one. + + Netmask: The IP Netmask. This field is only valid if the static + field is set to one. + + Gateway: The IP address of the gateway. This field is only valid + if the static field is set to one. + + Static: An 8-bit Boolean stating whether or not the WTP should use + a static IP address. A value of zero disables the static IP + address, while a value of one enables it. + +4.7. CAPWAP Protocol Timers + + This section contains the definition of the CAPWAP timers. + +4.7.1. ChangeStatePendingTimer + + The maximum time, in seconds, the AC will wait for the Change State + Event Request from the WTP after having transmitted a successful + Configuration Status Response message. + + Default: 25 seconds + +4.7.2. DataChannelKeepAlive + + The DataChannelKeepAlive timer is used by the WTP to determine the + next opportunity when it must transmit the Data Channel Keep-Alive, + in seconds. + + Default: 30 seconds + + + +Calhoun, et al. Standards Track [Page 98] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.7.3. DataChannelDeadInterval + + The minimum time, in seconds, a WTP MUST wait without having received + a Data Channel Keep-Alive packet before the destination for the Data + Channel Keep-Alive packets may be considered dead. The value of this + timer MUST be no less than 2*DataChannelKeepAlive seconds and no + greater that 240 seconds. + + Default: 60 + +4.7.4. DataCheckTimer + + The number of seconds the AC will wait for the Data Channel Keep + Alive, which is required by the CAPWAP state machine's Data Check + state. The AC resets the state machine if this timer expires prior + to transitioning to the next state. + + Default: 30 + +4.7.5. DiscoveryInterval + + The minimum time, in seconds, that a WTP MUST wait after receiving a + Discovery Response message, before initiating a DTLS handshake. + + Default: 5 + +4.7.6. DTLSSessionDelete + + The minimum time, in seconds, a WTP MUST wait for DTLS session + deletion. + + Default: 5 + +4.7.7. EchoInterval + + The minimum time, in seconds, between sending Echo Request messages + to the AC with which the WTP has joined. + + Default: 30 + +4.7.8. IdleTimeout + + The default Idle Timeout is 300 seconds. + + + + + + + + +Calhoun, et al. Standards Track [Page 99] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.7.9. ImageDataStartTimer + + The number of seconds the WTP will wait for its peer to transmit the + Image Data Request. + + Default: 30 + +4.7.10. MaxDiscoveryInterval + + The maximum time allowed between sending Discovery Request messages, + in seconds. This value MUST be no less than 2 seconds and no greater + than 180 seconds. + + Default: 20 seconds. + +4.7.11. ReportInterval + + The ReportInterval is used by the WTP to determine the interval the + WTP uses between sending the Decryption Error message elements to + inform the AC of decryption errors, in seconds. + + The default Report Interval is 120 seconds. + +4.7.12. RetransmitInterval + + The minimum time, in seconds, in which a non-acknowledged CAPWAP + packet will be retransmitted. + + Default: 3 + +4.7.13. SilentInterval + + For a WTP, this is the minimum time, in seconds, a WTP MUST wait + before it MAY again send Discovery Request messages or attempt to + establish a DTLS session. For an AC, this is the minimum time, in + seconds, during which the AC SHOULD ignore all CAPWAP and DTLS + packets received from the WTP that is in the Sulking state. + + Default: 30 seconds + +4.7.14. StatisticsTimer + + The StatisticsTimer is used by the WTP to determine the interval the + WTP uses between the WTP Events Requests it transmits to the AC to + communicate its statistics, in seconds. + + Default: 120 seconds + + + + +Calhoun, et al. Standards Track [Page 100] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.7.15. WaitDTLS + + The maximum time, in seconds, a WTP MUST wait without having received + a DTLS Handshake message from an AC. This timer MUST be greater than + 30 seconds. + + Default: 60 + +4.7.16. WaitJoin + + The maximum time, in seconds, an AC will wait after the DTLS session + has been established until it receives the Join Request from the WTP. + This timer MUST be greater than 20 seconds. + + Default: 60 + +4.8. CAPWAP Protocol Variables + + This section defines the CAPWAP protocol variables, which are used + for various protocol functions. Some of these variables are + configurable, while others are counters or have a fixed value. For + non-counter-related variables, default values are specified. + However, when a WTP's variable configuration is explicitly overridden + by an AC, the WTP MUST save the new value. + +4.8.1. AdminState + + The default Administrative State value is enabled (1). + +4.8.2. DiscoveryCount + + The number of Discovery Request messages transmitted by a WTP to a + single AC. This is a monotonically increasing counter. + +4.8.3. FailedDTLSAuthFailCount + + The number of failed DTLS session establishment attempts due to + authentication failures. + +4.8.4. FailedDTLSSessionCount + + The number of failed DTLS session establishment attempts. + + + + + + + + + +Calhoun, et al. Standards Track [Page 101] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.8.5. MaxDiscoveries + + The maximum number of Discovery Request messages that will be sent + after a WTP boots. + + Default: 10 + +4.8.6. MaxFailedDTLSSessionRetry + + The maximum number of failed DTLS session establishment attempts + before the CAPWAP device enters a silent period. + + Default: 3 + +4.8.7. MaxRetransmit + + The maximum number of retransmissions for a given CAPWAP packet + before the link layer considers the peer dead. + + Default: 5 + +4.8.8. RetransmitCount + + The number of retransmissions for a given CAPWAP packet. This is a + monotonically increasing counter. + +4.8.9. WTPFallBack + + The default WTP Fallback value is enabled (1). + +4.9. WTP Saved Variables + + In addition to the values defined in Section 4.8, the following + values SHOULD be saved on the WTP in non-volatile memory. CAPWAP + wireless bindings MAY define additional values that SHOULD be stored + on the WTP. + +4.9.1. AdminRebootCount + + The number of times the WTP has rebooted administratively, defined in + Section 4.6.47. + +4.9.2. FrameEncapType + + For WTPs that support multiple Frame Encapsulation Types, it is + useful to save the value configured by the AC. The Frame + Encapsulation Type is defined in Section 4.6.43. + + + + +Calhoun, et al. Standards Track [Page 102] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +4.9.3. LastRebootReason + + The reason why the WTP last rebooted, defined in Section 4.6.47. + +4.9.4. MacType + + For WTPs that support multiple MAC-Types, it is useful to save the + value configured by the AC. The MAC-Type is defined in + Section 4.6.44. + +4.9.5. PreferredACs + + The preferred ACs, with the index, defined in Section 4.6.5. + +4.9.6. RebootCount + + The number of times the WTP has rebooted, defined in Section 4.6.47. + +4.9.7. Static IP Address + + The static IP address assigned to the WTP, as configured by the WTP + Static IP address Information message element (see Section 4.6.48). + +4.9.8. WTPLinkFailureCount + + The number of times the link to the AC has failed, see + Section 4.6.47. + +4.9.9. WTPLocation + + The WTP Location, defined in Section 4.6.30. + +4.9.10. WTPName + + The WTP Name, defined in Section 4.6.45. + +5. CAPWAP Discovery Operations + + The Discovery messages are used by a WTP to determine which ACs are + available to provide service, and the capabilities and load of the + ACs. + +5.1. Discovery Request Message + + The Discovery Request message is used by the WTP to automatically + discover potential ACs available in the network. The Discovery + Request message provides ACs with the primary capabilities of the + + + + +Calhoun, et al. Standards Track [Page 103] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + WTP. A WTP must exchange this information to ensure subsequent + exchanges with the ACs are consistent with the WTP's functional + characteristics. + + Discovery Request messages MUST be sent by a WTP in the Discover + state after waiting for a random delay less than + MaxDiscoveryInterval, after a WTP first comes up or is + (re)initialized. A WTP MUST send no more than the maximum of + MaxDiscoveries Discovery Request messages, waiting for a random delay + less than MaxDiscoveryInterval between each successive message. + + This is to prevent an explosion of WTP Discovery Request messages. + An example of this occurring is when many WTPs are powered on at the + same time. + + If a Discovery Response message is not received after sending the + maximum number of Discovery Request messages, the WTP enters the + Sulking state and MUST wait for an interval equal to SilentInterval + before sending further Discovery Request messages. + + Upon receiving a Discovery Request message, the AC will respond with + a Discovery Response message sent to the address in the source + address of the received Discovery Request message. Once a Discovery + Response has been received, if the WTP decides to establish a session + with the responding AC, it SHOULD perform an MTU discovery, using the + process described in Section 3.5. + + It is possible for the AC to receive a clear text Discovery Request + message while a DTLS session is already active with the WTP. This is + most likely the case if the WTP has rebooted, perhaps due to a + software or power failure, but could also be caused by a DoS attack. + In such cases, any WTP state, including the state machine instance, + MUST NOT be cleared until another DTLS session has been successfully + established, communicated via the DTLSSessionEstablished DTLS + notification (see Section 2.3.2.2). + + The binding specific WTP Radio Information message element (see + Section 2.1) is included in the Discovery Request message to + advertise WTP support for one or more CAPWAP bindings. + + The Discovery Request message is sent by the WTP when in the + Discovery state. The AC does not transmit this message. + + The following message elements MUST be included in the Discovery + Request message: + + o Discovery Type, see Section 4.6.21 + + + + +Calhoun, et al. Standards Track [Page 104] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + o WTP Board Data, see Section 4.6.40 + + o WTP Descriptor, see Section 4.6.41 + + o WTP Frame Tunnel Mode, see Section 4.6.43 + + o WTP MAC Type, see Section 4.6.44 + + o WTP Radio Information message element(s) that the WTP supports; + These are defined by the individual link layer CAPWAP Binding + Protocols (see Section 2.1). + + The following message elements MAY be included in the Discovery + Request message: + + o MTU Discovery Padding, see Section 4.6.32 + + o Vendor Specific Payload, see Section 4.6.39 + +5.2. Discovery Response Message + + The Discovery Response message provides a mechanism for an AC to + advertise its services to requesting WTPs. + + When a WTP receives a Discovery Response message, it MUST wait for an + interval not less than DiscoveryInterval for receipt of additional + Discovery Response messages. After the DiscoveryInterval elapses, + the WTP enters the DTLS-Init state and selects one of the ACs that + sent a Discovery Response message and send a DTLS Handshake to that + AC. + + One or more binding-specific WTP Radio Information message elements + (see Section 2.1) are included in the Discovery Request message to + advertise AC support for the CAPWAP bindings. The AC MAY include + only the bindings it shares in common with the WTP, known through the + WTP Radio Information message elements received in the Discovery + Request message, or it MAY include all of the bindings supported. + The WTP MAY use the supported bindings in its AC decision process. + Note that if the WTP joins an AC that does not support a specific + CAPWAP binding, service for that binding MUST NOT be provided by the + WTP. + + The Discovery Response message is sent by the AC when in the Idle + state. The WTP does not transmit this message. + + The following message elements MUST be included in the Discovery + Response Message: + + + + +Calhoun, et al. Standards Track [Page 105] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + o AC Descriptor, see Section 4.6.1 + + o AC Name, see Section 4.6.4 + + o WTP Radio Information message element(s) that the AC supports; + these are defined by the individual link layer CAPWAP Binding + Protocols (see Section 2.1 for more information). + + o One of the following message elements MUST be included in the + Discovery Response Message: + + * CAPWAP Control IPv4 Address, see Section 4.6.9 + + * CAPWAP Control IPv6 Address, see Section 4.6.10 + + The following message elements MAY be included in the Discovery + Response message: + + o Vendor Specific Payload, see Section 4.6.39 + +5.3. Primary Discovery Request Message + + The Primary Discovery Request message is sent by the WTP to: + + o determine whether its preferred (or primary) AC is available, or + + o perform a Path MTU Discovery (see Section 3.5). + + A Primary Discovery Request message is sent by a WTP when it has a + primary AC configured, and is connected to another AC. This + generally occurs as a result of a failover, and is used by the WTP as + a means to discover when its primary AC becomes available. Since the + WTP only has a single instance of the CAPWAP state machine, the + Primary Discovery Request is sent by the WTP when in the Run state. + The AC does not transmit this message. + + The frequency of the Primary Discovery Request messages should be no + more often than the sending of the Echo Request message. + + Upon receipt of a Primary Discovery Request message, the AC responds + with a Primary Discovery Response message sent to the address in the + source address of the received Primary Discovery Request message. + + The following message elements MUST be included in the Primary + Discovery Request message. + + o Discovery Type, see Section 4.6.21 + + + + +Calhoun, et al. Standards Track [Page 106] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + o WTP Board Data, see Section 4.6.40 + + o WTP Descriptor, see Section 4.6.41 + + o WTP Frame Tunnel Mode, see Section 4.6.43 + + o WTP MAC Type, see Section 4.6.44 + + o WTP Radio Information message element(s) that the WTP supports; + these are defined by the individual link layer CAPWAP Binding + Protocols (see Section 2.1 for more information). + + The following message elements MAY be included in the Primary + Discovery Request message: + + o MTU Discovery Padding, see Section 4.6.32 + + o Vendor Specific Payload, see Section 4.6.39 + +5.4. Primary Discovery Response + + The Primary Discovery Response message enables an AC to advertise its + availability and services to requesting WTPs that are configured to + have the AC as its primary AC. + + The Primary Discovery Response message is sent by an AC after + receiving a Primary Discovery Request message. + + When a WTP receives a Primary Discovery Response message, it may + establish a CAPWAP protocol connection to its primary AC, based on + the configuration of the WTP Fallback Status message element on the + WTP. + + The Primary Discovery Response message is sent by the AC when in the + Idle state. The WTP does not transmit this message. + + The following message elements MUST be included in the Primary + Discovery Response message. + + o AC Descriptor, see Section 4.6.1 + + o AC Name, see Section 4.6.4 + + o WTP Radio Information message element(s) that the AC supports; + These are defined by the individual link layer CAPWAP Binding + Protocols (see Section 2.1 for more information). + + + + + +Calhoun, et al. Standards Track [Page 107] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + One of the following message elements MUST be included in the + Discovery Response Message: + + o CAPWAP Control IPv4 Address, see Section 4.6.9 + + o CAPWAP Control IPv6 Address, see Section 4.6.10 + + The following message elements MAY be included in the Primary + Discovery Response message: + + o Vendor Specific Payload, see Section 4.6.39 + +6. CAPWAP Join Operations + + The Join Request message is used by a WTP to request service from an + AC after a DTLS connection is established to that AC. The Join + Response message is used by the AC to indicate that it will or will + not provide service. + +6.1. Join Request + + The Join Request message is used by a WTP to request service through + the AC. If the WTP is performing the optional AC Discovery process + (see Section 3.3), the join process occurs after the WTP has received + one or more Discovery Response messages. During the Discovery + process, an AC MAY return more than one CAPWAP Control IPv4 Address + or CAPWAP Control IPv6 Address message elements. When more than one + such message element is returned, the WTP SHOULD perform "load + balancing" by choosing the interface that is servicing the least + number of WTPs (known through the WTP Count field of the message + element). Note, however, that other load balancing algorithms are + also permitted. Once the WTP has determined its preferred AC, and + its associated interface, to which to connect, it establishes the + DTLS session, and transmits the Join Request over the secured control + channel. When an AC receives a Join Request message it responds with + a Join Response message. + + Upon completion of the DTLS handshake and receipt of the + DTLSEstablished notification, the WTP sends the Join Request message + to the AC. When the AC is notified of the DTLS session + establishment, it does not clear the WaitDTLS timer until it has + received the Join Request message, at which time it sends a Join + Response message to the WTP, indicating success or failure. + + One or more WTP Radio Information message elements (see Section 2.1) + are included in the Join Request to request service for the CAPWAP + bindings by the AC. Including a binding that is unsupported by the + AC will result in a failed Join Response. + + + +Calhoun, et al. Standards Track [Page 108] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + If the AC rejects the Join Request, it sends a Join Response message + with a failure indication and initiates an abort of the DTLS session + via the DTLSAbort command. + + If an invalid (i.e., malformed) Join Request message is received, the + message MUST be silently discarded by the AC. No response is sent to + the WTP. The AC SHOULD log this event. + + The Join Request is sent by the WTP when in the Join State. The AC + does not transmit this message. + + The following message elements MUST be included in the Join Request + message. + + o Location Data, see Section 4.6.30 + + o WTP Board Data, see Section 4.6.40 + + o WTP Descriptor, see Section 4.6.41 + + o WTP Name, see Section 4.6.45 + + o Session ID, see Section 4.6.37 + + o WTP Frame Tunnel Mode, see Section 4.6.43 + + o WTP MAC Type, see Section 4.6.44 + + o WTP Radio Information message element(s) that the WTP supports; + these are defined by the individual link layer CAPWAP Binding + Protocols (see Section 2.1 for more information). + + o ECN Support, see Section 4.6.25 + + At least one of the following message element MUST be included in the + Join Request message. + + o CAPWAP Local IPv4 Address, see Section 4.6.11 + + o CAPWAP Local IPv6 Address, see Section 4.6.12 + + The following message element MAY be included in the Join Request + message. + + o CAPWAP Transport Protocol, see Section 4.6.14 + + o Maximum Message Length, see Section 4.6.31 + + + + +Calhoun, et al. Standards Track [Page 109] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + o WTP Reboot Statistics, see Section 4.6.47 + + o Vendor Specific Payload, see Section 4.6.39 + +6.2. Join Response + + The Join Response message is sent by the AC to indicate to a WTP that + it is capable and willing to provide service to the WTP. + + The WTP, receiving a Join Response message, checks for success or + failure. If the message indicates success, the WTP clears the + WaitDTLS timer for the session and proceeds to the Configure state. + + If the WaitDTLS Timer expires prior to reception of the Join Response + message, the WTP MUST terminate the handshake, deallocate session + state and initiate the DTLSAbort command. + + If an invalid (malformed) Join Response message is received, the WTP + SHOULD log an informative message detailing the error. This error + MUST be treated in the same manner as AC non-responsiveness. The + WaitDTLS timer will eventually expire, and the WTP MAY (if it is so + configured) attempt to join a new AC. + + If one of the WTP Radio Information message elements (see + Section 2.1) in the Join Request message requested support for a + CAPWAP binding that the AC does not support, the AC sets the Result + Code message element to "Binding Not Supported". + + The AC includes the Image Identifier message element to indicate the + software version it expects the WTP to run. This information is used + to determine whether the WTP MUST change its currently running + firmware image or download a new version (see Section 9.1.1). + + The Join Response message is sent by the AC when in the Join State. + The WTP does not transmit this message. + + The following message elements MUST be included in the Join Response + message. + + o Result Code, see Section 4.6.35 + + o AC Descriptor, see Section 4.6.1 + + o AC Name, see Section 4.6.4 + + o WTP Radio Information message element(s) that the AC supports; + these are defined by the individual link layer CAPWAP Binding + Protocols (see Section 2.1). + + + +Calhoun, et al. Standards Track [Page 110] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + o ECN Support, see Section 4.6.25 + + One of the following message elements MUST be included in the Join + Response Message: + + o CAPWAP Control IPv4 Address, see Section 4.6.9 + + o CAPWAP Control IPv6 Address, see Section 4.6.10 + + One of the following message elements MUST be included in the Join + Response Message: + + o CAPWAP Local IPv4 Address, see Section 4.6.11 + + o CAPWAP Local IPv6 Address, see Section 4.6.12 + + The following message elements MAY be included in the Join Response + message. + + o AC IPv4 List, see Section 4.6.2 + + o AC IPv6 List, see Section 4.6.3 + + o CAPWAP Transport Protocol, see Section 4.6.14 + + o Image Identifier, see Section 4.6.27 + + o Maximum Message Length, see Section 4.6.31 + + o Vendor Specific Payload, see Section 4.6.39 + +7. Control Channel Management + + The Control Channel Management messages are used by the WTP and AC to + maintain a control communication channel. CAPWAP Control messages, + such as the WTP Event Request message sent from the WTP to the AC + indicate to the AC that the WTP is operational. When such control + messages are not being sent, the Echo Request and Echo Response + messages are used to maintain the control communication channel. + +7.1. Echo Request + + The Echo Request message is a keep-alive mechanism for CAPWAP control + messages. + + + + + + + +Calhoun, et al. Standards Track [Page 111] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Echo Request messages are sent periodically by a WTP in the Image + Data or Run state (see Section 2.3) to determine the state of the + control connection between the WTP and the AC. The Echo Request + message is sent by the WTP when the EchoInterval timer expires. + + The Echo Request message is sent by the WTP when in the Run state. + The AC does not transmit this message. + + The following message elements MAY be included in the Echo Request + message: + + o Vendor Specific Payload, see Section 4.6.39 + + When an AC receives an Echo Request message it responds with an Echo + Response message. + +7.2. Echo Response + + The Echo Response message acknowledges the Echo Request message. + + An Echo Response message is sent by an AC after receiving an Echo + Request message. After transmitting the Echo Response message, the + AC SHOULD reset its EchoInterval timer (see Section 4.7.7). If + another Echo Request message or other control message is not received + by the AC when the timer expires, the AC SHOULD consider the WTP to + be no longer reachable. + + The Echo Response message is sent by the AC when in the Run state. + The WTP does not transmit this message. + + The following message elements MAY be included in the Echo Response + message: + + o Vendor Specific Payload, see Section 4.6.39 + + When a WTP receives an Echo Response message it initializes the + EchoInterval to the configured value. + +8. WTP Configuration Management + + WTP Configuration messages are used to exchange configuration + information between the AC and the WTP. + +8.1. Configuration Consistency + + The CAPWAP protocol provides flexibility in how WTP configuration is + managed. A WTP can behave in one of two ways, which is + implementation specific: + + + +Calhoun, et al. Standards Track [Page 112] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + 1. The WTP retains no configuration and accepts the configuration + provided by the AC. + + 2. The WTP saves the configuration of parameters provided by the AC + that are non-default values into local non-volatile memory, and + are enforced during the WTP's power up initialization phase. + + If the WTP opts to save configuration locally, the CAPWAP protocol + state machine defines the Configure state, which allows for + configuration exchange. In the Configure state, the WTP sends its + current configuration overrides to the AC via the Configuration + Status Request message. A configuration override is a non-default + parameter. As an example, in the CAPWAP protocol, the default + antenna configuration is internal omni antenna. A WTP that either + has no internal antennas, or has been explicitly configured by the AC + to use external antennas, sends its antenna configuration during the + configure phase, allowing the AC to become aware of the WTP's current + configuration. + + Once the WTP has provided its configuration to the AC, the AC sends + its configuration to the WTP. This allows the WTP to receive + configuration and policies from the AC. + + The AC maintains a copy of each active WTP configuration. There is + no need for versioning or other means to identify configuration + changes. If a WTP becomes inactive, the AC MAY delete the inactive + WTP configuration. If a WTP fails, and connects to a new AC, the WTP + provides its overridden configuration parameters, allowing the new AC + to be aware of the WTP configuration. + + This model allows for resiliency in case of an AC failure, ensuring + another AC can provide service to the WTP. A new AC would be + automatically updated with WTP configuration changes, eliminating the + need for inter-AC communication and the need for all ACs to be aware + of the configuration of all WTPs in the network. + + Once the CAPWAP protocol enters the Run state, the WTPs begin to + provide service. It is common for administrators to require that + configuration changes be made while the network is operational. + Therefore, the Configuration Update Request is sent by the AC to the + WTP to make these changes at run-time. + +8.1.1. Configuration Flexibility + + The CAPWAP protocol provides the flexibility to configure and manage + WTPs of varying design and functional characteristics. When a WTP + first discovers an AC, it provides primary functional information + + + + +Calhoun, et al. Standards Track [Page 113] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + relating to its type of MAC and to the nature of frames to be + exchanged. The AC configures the WTP appropriately. The AC also + establishes corresponding internal state for the WTP. + +8.2. Configuration Status Request + + The Configuration Status Request message is sent by a WTP to deliver + its current configuration to the AC. + + The Configuration Status Request message carries binding-specific + message elements. Refer to the appropriate binding for the + definition of this structure. + + When an AC receives a Configuration Status Request message, it acts + upon the content of the message and responds to the WTP with a + Configuration Status Response message. + + The Configuration Status Request message includes multiple Radio + Administrative State message elements, one for the WTP, and one for + each radio in the WTP. + + The Configuration Status Request message is sent by the WTP when in + the Configure State. The AC does not transmit this message. + + The following message elements MUST be included in the Configuration + Status Request message. + + o AC Name, see Section 4.6.4 + + o Radio Administrative State, see Section 4.6.33 + + o Statistics Timer, see Section 4.6.38 + + o WTP Reboot Statistics, see Section 4.6.47 + + The following message elements MAY be included in the Configuration + Status Request message. + + o AC Name with Priority, see Section 4.6.5 + + o CAPWAP Transport Protocol, see Section 4.6.14 + + o WTP Static IP Address Information, see Section 4.6.48 + + o Vendor Specific Payload, see Section 4.6.39 + + + + + + +Calhoun, et al. Standards Track [Page 114] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +8.3. Configuration Status Response + + The Configuration Status Response message is sent by an AC and + provides a mechanism for the AC to override a WTP's requested + configuration. + + A Configuration Status Response message is sent by an AC after + receiving a Configuration Status Request message. + + The Configuration Status Response message carries binding-specific + message elements. Refer to the appropriate binding for the + definition of this structure. + + When a WTP receives a Configuration Status Response message, it acts + upon the content of the message, as appropriate. If the + Configuration Status Response message includes a Radio Operational + State message element that causes a change in the operational state + of one of the radios, the WTP transmits a Change State Event to the + AC, as an acknowledgement of the change in state. + + The Configuration Status Response message is sent by the AC when in + the Configure state. The WTP does not transmit this message. + + The following message elements MUST be included in the Configuration + Status Response message. + + o CAPWAP Timers, see Section 4.6.13 + + o Decryption Error Report Period, see Section 4.6.18 + + o Idle Timeout, see Section 4.6.24 + + o WTP Fallback, see Section 4.6.42 + + One or both of the following message elements MUST be included in the + Configuration Status Response message: + + o AC IPv4 List, see Section 4.6.2 + + o AC IPv6 List, see Section 4.6.3 + + The following message element MAY be included in the Configuration + Status Response message. + + o WTP Static IP Address Information, see Section 4.6.48 + + o Vendor Specific Payload, see Section 4.6.39 + + + + +Calhoun, et al. Standards Track [Page 115] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +8.4. Configuration Update Request + + Configuration Update Request messages are sent by the AC to provision + the WTP while in the Run state. This is used to modify the + configuration of the WTP while it is operational. + + When a WTP receives a Configuration Update Request message, it + responds with a Configuration Update Response message, with a Result + Code message element indicating the result of the configuration + request. + + The AC includes the Image Identifier message element (see + Section 4.6.27) to force the WTP to update its firmware while in the + Run state. The WTP MAY proceed to download the requested firmware if + it determines the version specified in the Image Identifier message + element is not in its non-volatile storage by transmitting an Image + Data Request (see Section 9.1.1) that includes the Initiate Download + message element (see Section 4.6.29). + + The Configuration Update Request is sent by the AC when in the Run + state. The WTP does not transmit this message. + + One or more of the following message elements MAY be included in the + Configuration Update message: + + o AC Name with Priority, see Section 4.6.5 + + o AC Timestamp, see Section 4.6.6 + + o Add MAC ACL Entry, see Section 4.6.7 + + o CAPWAP Timers, see Section 4.6.13 + + o Decryption Error Report Period, see Section 4.6.18 + + o Delete MAC ACL Entry, see Section 4.6.19 + + o Idle Timeout, see Section 4.6.24 + + o Location Data, see Section 4.6.30 + + o Radio Administrative State, see Section 4.6.33 + + o Statistics Timer, see Section 4.6.38 + + o WTP Fallback, see Section 4.6.42 + + o WTP Name, see Section 4.6.45 + + + +Calhoun, et al. Standards Track [Page 116] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + o WTP Static IP Address Information, see Section 4.6.48 + + o Image Identifier, see Section 4.6.27 + + o Vendor Specific Payload, see Section 4.6.39 + +8.5. Configuration Update Response + + The Configuration Update Response message is the acknowledgement + message for the Configuration Update Request message. + + The Configuration Update Response message is sent by a WTP after + receiving a Configuration Update Request message. + + When an AC receives a Configuration Update Response message, the + result code indicates if the WTP successfully accepted the + configuration. + + The Configuration Update Response message is sent by the WTP when in + the Run state. The AC does not transmit this message. + + The following message element MUST be present in the Configuration + Update message. + + Result Code, see Section 4.6.35 + + The following message elements MAY be present in the Configuration + Update Response message. + + o Radio Operational State, see Section 4.6.34 + + o Vendor Specific Payload, see Section 4.6.39 + +8.6. Change State Event Request + + The Change State Event Request message is used by the WTP for two + main purposes: + + o When sent by the WTP following the reception of a Configuration + Status Response message from the AC, the WTP uses the Change State + Event Request message to provide an update on the WTP radio's + operational state and to confirm that the configuration provided + by the AC was successfully applied. + + o When sent during the Run state, the WTP uses the Change State + Event Request message to notify the AC of an unexpected change in + the WTP's radio operational state. + + + + +Calhoun, et al. Standards Track [Page 117] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + When an AC receives a Change State Event Request message it responds + with a Change State Event Response message and modifies its data + structures for the WTP as needed. The AC MAY decide not to provide + service to the WTP if it receives an error, based on local policy, + and to transition to the Reset state. + + The Change State Event Request message is sent by a WTP to + acknowledge or report an error condition to the AC for a requested + configuration in the Configuration Status Response message. The + Change State Event Request message includes the Result Code message + element, which indicates whether the configuration was successfully + applied. If the WTP is unable to apply a specific configuration + request, it indicates the failure by including one or more Returned + Message Element message elements (see Section 4.6.36). + + The Change State Event Request message is sent by the WTP in the + Configure or Run state. The AC does not transmit this message. + + The WTP MAY save its configuration to persistent storage prior to + transmitting the response. However, this is implementation specific + and is not required. + + The following message elements MUST be present in the Change State + Event Request message. + + o Radio Operational State, see Section 4.6.34 + + o Result Code, see Section 4.6.35 + + One or more of the following message elements MAY be present in the + Change State Event Request message: + + o Returned Message Element(s), see Section 4.6.36 + + o Vendor Specific Payload, see Section 4.6.39 + +8.7. Change State Event Response + + The Change State Event Response message acknowledges the Change State + Event Request message. + + A Change State Event Response message is sent by an AC in response to + a Change State Event Request message. + + The Change State Event Response message is sent by the AC when in the + Configure or Run state. The WTP does not transmit this message. + + + + + +Calhoun, et al. Standards Track [Page 118] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + The following message element MAY be included in the Change State + Event Response message: + + o Vendor Specific Payload, see Section 4.6.39 + + The WTP does not take any action upon receipt of the Change State + Event Response message. + +8.8. Clear Configuration Request + + The Clear Configuration Request message is used to reset the WTP + configuration. + + The Clear Configuration Request message is sent by an AC to request + that a WTP reset its configuration to the manufacturing default + configuration. The Clear Config Request message is sent while in the + Run state. + + The Clear Configuration Request is sent by the AC when in the Run + state. The WTP does not transmit this message. + + The following message element MAY be included in the Clear + Configuration Request message: + + o Vendor Specific Payload, see Section 4.6.39 + + When a WTP receives a Clear Configuration Request message, it resets + its configuration to the manufacturing default configuration. + +8.9. Clear Configuration Response + + The Clear Configuration Response message is sent by the WTP after + receiving a Clear Configuration Request message and resetting its + configuration parameters to the manufacturing default values. + + The Clear Configuration Response is sent by the WTP when in the Run + state. The AC does not transmit this message. + + The Clear Configuration Response message MUST include the following + message element: + + o Result Code, see Section 4.6.35 + + The following message element MAY be included in the Clear + Configuration Request message: + + o Vendor Specific Payload, see Section 4.6.39 + + + + +Calhoun, et al. Standards Track [Page 119] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +9. Device Management Operations + + This section defines CAPWAP operations responsible for debugging, + gathering statistics, logging, and firmware management. The + management operations defined in this section are used by the AC to + either push/pull information to/from the WTP, or request that the WTP + reboot. This section does not deal with the management of the AC per + se, and assumes that the AC is operational and configured. + +9.1. Firmware Management + + This section describes the firmware download procedures used by the + CAPWAP protocol. Firmware download can occur during the Image Data + or Run state. The former allows the download to occur at boot time, + while the latter is used to trigger the download while an active + CAPWAP session exists. It is important to note that the CAPWAP + protocol does not provide the ability for the AC to identify whether + the firmware information provided by the WTP is correct or whether + the WTP is properly storing the firmware (see Section 12.10 for more + information). + + Figure 6 provides an example of a WTP that performs a firmware + upgrade while in the Image Data state. In this example, the WTP does + not already have the requested firmware (Image Identifier = x), and + downloads the image from the AC. + + + + + + + + + + + + + + + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 120] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + WTP AC + + Join Request + --------------------------------------------------------> + + Join Response (Image Identifier = x) + <------------------------------------------------------ + + Image Data Request (Image Identifier = x, + Initiate Download) + --------------------------------------------------------> + + Image Data Response (Result Code = Success, + Image Information = {size,hash}) + <------------------------------------------------------ + + Image Data Request (Image Data = Data) + <------------------------------------------------------ + + Image Data Response (Result Code = Success) + --------------------------------------------------------> + + ..... + + Image Data Request (Image Data = EOF) + <------------------------------------------------------ + + Image Data Response (Result Code = Success) + --------------------------------------------------------> + + (WTP enters the Reset State) + + Figure 6: WTP Firmware Download Case 1 + + Figure 7 provides an example in which the WTP has the image specified + by the AC in its non-volatile storage, but is not its current running + image. In this case, the WTP opts to NOT download the firmware and + immediately reset to the requested image. + + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 121] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + WTP AC + + Join Request + --------------------------------------------------------> + + Join Response (Image Identifier = x) + <------------------------------------------------------ + + (WTP enters the Reset State) + + Figure 7: WTP Firmware Download Case 2 + + Figure 8 provides an example of a WTP that performs a firmware + upgrade while in the Run state. This mode of firmware upgrade allows + the WTP to download its image while continuing to provide service. + The WTP will not automatically reset until it is notified by the AC, + with a Reset Request message. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 122] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + WTP AC + + Configuration Update Request (Image Identifier = x) + <------------------------------------------------------ + + Configuration Update Response (Result Code = Success) + --------------------------------------------------------> + + + Image Data Request (Image Identifier = x, + Initiate Download) + --------------------------------------------------------> + + Image Data Response (Result Code = Success, + Image Information = {size,hash}) + <------------------------------------------------------ + + Image Data Request (Image Data = Data) + <------------------------------------------------------ + + Image Data Response (Result Code = Success) + --------------------------------------------------------> + + ..... + + Image Data Request (Image Data = EOF) + <------------------------------------------------------ + + Image Data Response (Result Code = Success) + --------------------------------------------------------> + + ..... + + (administratively requested reboot request) + Reset Request (Image Identifier = x) + <------------------------------------------------------ + + Reset Response (Result Code = Success) + --------------------------------------------------------> + + Figure 8: WTP Firmware Download Case 3 + + Figure 9 provides another example of the firmware download while in + the Run state. In this example, the WTP already has the image + specified by the AC in its non-volatile storage. The WTP opts to NOT + download the firmware. The WTP resets upon receipt of a Reset + Request message from the AC. + + + + +Calhoun, et al. Standards Track [Page 123] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + WTP AC + + Configuration Update Request (Image Identifier = x) + <------------------------------------------------------ + + Configuration Update Response (Result Code = Already Have Image) + --------------------------------------------------------> + + ..... + + (administratively requested reboot request) + Reset Request (Image Identifier = x) + <------------------------------------------------------ + + Reset Response (Result Code = Success) + --------------------------------------------------------> + + Figure 9: WTP Firmware Download Case 4 + +9.1.1. Image Data Request + + The Image Data Request message is used to update firmware on the WTP. + This message and its companion Response message are used by the AC to + ensure that the image being run on each WTP is appropriate. + + Image Data Request messages are exchanged between the WTP and the AC + to download a new firmware image to the WTP. When a WTP or AC + receives an Image Data Request message, it responds with an Image + Data Response message. The message elements contained within the + Image Data Request message are required to determine the intent of + the request. + + The decision that new firmware is to be downloaded to the WTP can + occur in one of two ways: + + When the WTP joins the AC, the Join Response message includes the + Image Identifier message element, which informs the WTP of the + firmware it is expected to run. If the WTP does not currently + have the requested firmware version, it transmits an Image Data + Request message, with the appropriate Image Identifier message + element. If the WTP already has the requested firmware in its + non-volatile flash, but is not its currently running image, it + simply resets to run the proper firmware. + + Once the WTP is in the Run state, it is possible for the AC to + cause the WTP to initiate a firmware download by sending a + Configuration Update Request message with the Image Identifier + message elements. This will cause the WTP to transmit an Image + + + +Calhoun, et al. Standards Track [Page 124] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Data Request with the Image Identifier and the Initiate Download + message elements. Note that when the firmware is downloaded in + this way, the WTP does not automatically reset after the download + is complete. The WTP will only reset when it receives a Reset + Request message from the AC. If the WTP already had the requested + firmware version in its non-volatile storage, the WTP does not + transmit the Image Data Request message and responds with a + Configuration Update Response message with the Result Code set to + Image Already Present. + + Regardless of how the download was initiated, once the AC receives an + Image Data Request message with the Image Identifier message element, + it begins the transfer process by transmitting an Image Data Request + message that includes the Image Data message element. This continues + until the firmware image has been transferred. + + The Image Data Request message is sent by the WTP or the AC when in + the Image Data or Run state. + + The following message elements MAY be included in the Image Data + Request message: + + o CAPWAP Transport Protocol, see Section 4.6.14 + + o Image Data, see Section 4.6.26 + + o Vendor Specific Payload, see Section 4.6.39 + + The following message elements MAY be included in the Image Data + Request message when sent by the WTP: + + o Image Identifier, see Section 4.6.27 + + o Initiate Download, see Section 4.6.29 + +9.1.2. Image Data Response + + The Image Data Response message acknowledges the Image Data Request + message. + + An Image Data Response message is sent in response to a received + Image Data Request message. Its purpose is to acknowledge the + receipt of the Image Data Request message. The Result Code is + included to indicate whether a previously sent Image Data Request + message was invalid. + + The Image Data Response message is sent by the WTP or the AC when in + the Image Data or Run state. + + + +Calhoun, et al. Standards Track [Page 125] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + The following message element MUST be included in the Image Data + Response message: + + o Result Code, see Section 4.6.35 + + The following message element MAY be included in the Image Data + Response message: + + o Vendor Specific Payload, see Section 4.6.39 + + The following message element MAY be included in the Image Data + Response message when sent by the AC: + + o Image Information, see Section 4.6.28 + + Upon receiving an Image Data Response message indicating an error, + the WTP MAY retransmit a previous Image Data Request message, or + abandon the firmware download to the WTP by transitioning to the + Reset state. + +9.2. Reset Request + + The Reset Request message is used to cause a WTP to reboot. + + A Reset Request message is sent by an AC to cause a WTP to + reinitialize its operation. If the AC includes the Image Identifier + message element (see Section 4.6.27), it indicates to the WTP that it + SHOULD use that version of software upon reboot. + + The Reset Request is sent by the AC when in the Run state. The WTP + does not transmit this message. + + The following message element MUST be included in the Reset Request + message: + + o Image Identifier, see Section 4.6.27 + + The following message element MAY be included in the Reset Request + message: + + o Vendor Specific Payload, see Section 4.6.39 + + When a WTP receives a Reset Request message, it responds with a Reset + Response message indicating success and then reinitializes itself. + If the WTP is unable to write to its non-volatile storage, to ensure + that it runs the requested software version indicated in the Image + Identifier message element, it MAY send the appropriate Result Code + message element, but MUST reboot. If the WTP is unable to reset, + + + +Calhoun, et al. Standards Track [Page 126] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + including a hardware reset, it sends a Reset Response message to the + AC with a Result Code message element indicating failure. The AC no + longer provides service to the WTP. + +9.3. Reset Response + + The Reset Response message acknowledges the Reset Request message. + + A Reset Response message is sent by the WTP after receiving a Reset + Request message. + + The Reset Response is sent by the WTP when in the Run state. The AC + does not transmit this message. + + The following message elements MAY be included in the Reset Response + message. + + o Result Code, see Section 4.6.35 + + o Vendor Specific Payload, see Section 4.6.39 + + When an AC receives a successful Reset Response message, it is + notified that the WTP will reinitialize its operation. An AC that + receives a Reset Response message indicating failure may opt to no + longer provide service to the WTP. + +9.4. WTP Event Request + + The WTP Event Request message is used by a WTP to send information to + its AC. The WTP Event Request message MAY be sent periodically, or + sent in response to an asynchronous event on the WTP. For example, a + WTP MAY collect statistics and use the WTP Event Request message to + transmit the statistics to the AC. + + When an AC receives a WTP Event Request message it will respond with + a WTP Event Response message. + + The presence of the Delete Station message element is used by the WTP + to inform the AC that it is no longer providing service to the + station. This could be the result of an Idle Timeout (see + Section 4.6.24), due to resource shortages, or some other reason. + + The WTP Event Request message is sent by the WTP when in the Run + state. The AC does not transmit this message. + + + + + + + +Calhoun, et al. Standards Track [Page 127] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + The WTP Event Request message MUST contain one of the message + elements listed below, or a message element that is defined for a + specific wireless technology. More than one of each message element + listed MAY be included in the WTP Event Request message. + + o Decryption Error Report, see Section 4.6.17 + + o Duplicate IPv4 Address, see Section 4.6.22 + + o Duplicate IPv6 Address, see Section 4.6.23 + + o WTP Radio Statistics, see Section 4.6.46 + + o WTP Reboot Statistics, see Section 4.6.47 + + o Delete Station, see Section 4.6.20 + + o Vendor Specific Payload, see Section 4.6.39 + +9.5. WTP Event Response + + The WTP Event Response message acknowledges receipt of the WTP Event + Request message. + + A WTP Event Response message is sent by an AC after receiving a WTP + Event Request message. + + The WTP Event Response message is sent by the AC when in the Run + state. The WTP does not transmit this message. + + The following message element MAY be included in the WTP Event + Response message: + + o Vendor Specific Payload, see Section 4.6.39 + +9.6. Data Transfer + + This section describes the data transfer procedures used by the + CAPWAP protocol. The data transfer mechanism is used to upload + information available at the WTP to the AC, such as crash or debug + information. The data transfer messages can only be exchanged while + in the Run state. + + Figure 10 provides an example of an AC that requests that the WTP + transfer its latest crash file. Once the WTP acknowledges that it + has information to send, via the Data Transfer Response, it transmits + its own Data Transfer Request. Upon receipt, the AC responds with a + + + + +Calhoun, et al. Standards Track [Page 128] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Data Transfer Response, and the exchange continues until the WTP + transmits a Data Transfer Data message element that indicates an End + of File (EOF). + + WTP AC + + Data Transfer Request (Data Transfer Mode = Crash Data) + <------------------------------------------------------ + + Data Transfer Response (Result Code = Success) + --------------------------------------------------------> + + Data Transfer Request (Data Transfer Data = Data) + --------------------------------------------------------> + + Data Transfer Response (Result Code = Success) + <------------------------------------------------------ + + ..... + + Data Transfer Request (Data Transfer Data = EOF) + --------------------------------------------------------> + + Data Transfer Response (Result Code = Success) + <------------------------------------------------------ + + + Figure 10: WTP Data Transfer Case 1 + + Figure 11 provides an example of an AC that requests that the WTP + transfer its latest crash file. However, in this example, the WTP + does not have any crash information to send, and therefore sends a + Data Transfer Response with a Result Code indicating the error. + + WTP AC + + Data Transfer Request (Data Transfer Mode = Crash Data) + <------------------------------------------------------ + + Data Transfer Response (Result Code = Data Transfer + Error (No Information to Transfer)) + --------------------------------------------------------> + + + Figure 11: WTP Data Transfer Case 2 + + + + + + +Calhoun, et al. Standards Track [Page 129] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +9.6.1. Data Transfer Request + + The Data Transfer Request message is used to deliver debug + information from the WTP to the AC. + + The Data Transfer Request messages can be sent either by the AC or + the WTP. When sent by the AC, it is used to request that data be + transmitted from the WTP to the AC, and includes the Data Transfer + Mode message element, which specifies the information desired by the + AC. The Data Transfer Request is sent by the WTP in order to + transfer actual data to the AC, through the Data Transfer Data + message element. + + Given that the CAPWAP protocol minimizes the need for WTPs to be + directly managed, the Data Transfer Request is an important + troubleshooting tool used by the AC to retrieve information that may + be available on the WTP. For instance, some WTP implementations may + store crash information to help manufacturers identify software + faults. The Data Transfer Request message can be used to send such + information from the WTP to the AC. Another possible use would be to + allow a remote debugger function in the WTP to use the Data Transfer + Request message to send console output to the AC for debugging + purposes. + + When the WTP or AC receives a Data Transfer Request message, it + responds to the WTP with a Data Transfer Response message. The AC + MAY log the information received through the Data Transfer Data + message element. + + The Data Transfer Request message is sent by the WTP or AC when in + the Run state. + + When sent by the AC, the Data Transfer Request message MUST contain + the following message element: + + o Data Transfer Mode, see Section 4.6.16 + + When sent by the WTP, the Data Transfer Request message MUST contain + the following message element: + + o Data Transfer Data, see Section 4.6.15 + + Regardless of whether the Data Transfer Request is sent by the AC or + WTP, the following message element MAY be included in the Data + Transfer Request message: + + o Vendor Specific Payload, see Section 4.6.39 + + + + +Calhoun, et al. Standards Track [Page 130] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +9.6.2. Data Transfer Response + + The Data Transfer Response message acknowledges the Data Transfer + Request message. + + A Data Transfer Response message is sent in response to a received + Data Transfer Request message. Its purpose is to acknowledge receipt + of the Data Transfer Request message. When sent by the WTP, the + Result Code message element is used to indicate whether the data + transfer requested by the AC can be completed. When sent by the AC, + the Result Code message element is used to indicate receipt of the + data transferred in the Data Transfer Request message. + + The Data Transfer Response message is sent by the WTP or AC when in + the Run state. + + The following message element MUST be included in the Data Transfer + Response message: + + o Result Code, see Section 4.6.35 + + The following message element MAY be included in the Data Transfer + Response message: + + o Vendor Specific Payload, see Section 4.6.39 + + Upon receipt of a Data Transfer Response message, the WTP transmits + more information, if more information is available. + +10. Station Session Management + + Messages in this section are used by the AC to create, modify, or + delete station session state on the WTPs. + +10.1. Station Configuration Request + + The Station Configuration Request message is used to create, modify, + or delete station session state on a WTP. The message is sent by the + AC to the WTP, and MAY contain one or more message elements. The + message elements for this CAPWAP Control message include information + that is generally highly technology specific. Refer to the + appropriate binding document for definitions of the messages elements + that are included in this control message. + + The Station Configuration Request message is sent by the AC when in + the Run state. The WTP does not transmit this message. + + + + + +Calhoun, et al. Standards Track [Page 131] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + The following CAPWAP Control message elements MAY be included in the + Station Configuration Request message. More than one of each message + element listed MAY be included in the Station Configuration Request + message: + + o Add Station, see Section 4.6.8 + + o Delete Station, see Section 4.6.20 + + o Vendor Specific Payload, see Section 4.6.39 + +10.2. Station Configuration Response + + The Station Configuration Response message is used to acknowledge a + previously received Station Configuration Request message. + + The Station Configuration Response message is sent by the WTP when in + the Run state. The AC does not transmit this message. + + The following message element MUST be present in the Station + Configuration Response message: + + o Result Code, see Section 4.6.35 + + The following message element MAY be included in the Station + Configuration Response message: + + o Vendor Specific Payload, see Section 4.6.39 + + The Result Code message element indicates that the requested + configuration was successfully applied, or that an error related to + processing of the Station Configuration Request message occurred on + the WTP. + +11. NAT Considerations + + There are three specific situations in which a NAT deployment may be + used in conjunction with a CAPWAP-enabled deployment. The first + consists of a configuration in which a single WTP is behind a NAT + system. Since all communication is initiated by the WTP, and all + communication is performed over IP using two UDP ports, the protocol + easily traverses NAT systems in this configuration. + + In the second case, two or more WTPs are deployed behind the same NAT + system. Here, the AC would receive multiple connection requests from + the same IP address, and therefore cannot use the WTP's IP address + alone to bind the CAPWAP Control and Data channel. The CAPWAP Data + Check state, which establishes the data plane connection and + + + +Calhoun, et al. Standards Track [Page 132] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + communicates the CAPWAP Data Channel Keep-Alive, includes the Session + Identifier message element, which is used to bind the control and + data plane. Use of the Session Identifier message element enables + the AC to match the control and data plane flows from multiple WTPs + behind the same NAT system (multiple WTPs sharing the same IP + address). CAPWAP implementations MUST also use DTLS session + information on any encrypted CAPWAP channel to validate the source of + both the control and data plane, as described in Section 12.2. + + In the third configuration, the AC is deployed behind a NAT. In this + case, the AC is not reachable by the WTP unless a specific rule has + been configured on the NAT to translate the address and redirect + CAPWAP messages to the AC. This deployment presents two issues. + First, an AC communicates its interfaces and corresponding WTP load + using the CAPWAP Control IPv4 Address and CAPWAP Control IPv6 Address + message elements. This message element is mandatory, but contains IP + addresses that are only valid in the private address space used by + the AC, which is not reachable by the WTP. The WTP MUST NOT utilize + the information in these message elements if it detects a NAT (as + described in the CAPWAP Transport Protocol message element in + Section 4.6.14). Second, since the addresses cannot be used by the + WTP, this effectively disables the load-balancing capabilities (see + Section 6.1) of the CAPWAP protocol. Alternatively, the AC could + have a configured NAT'ed address, which it would include in either of + the two control address message elements, and the NAT would need to + be configured accordingly. + + In order for a CAPWAP WTP or AC to detect whether a middlebox is + present, both the Join Request (see Section 6.1) and the Join + Response (see Section 6.2) include either the CAPWAP Local IPv4 + Address (see Section 4.6.11) or the CAPWAP Local IPv6 Address (see + Section 4.6.12) message element. Upon receiving one of these + messages, if the packet's source IP address differs from the address + found in either one of these message elements, it indicates that a + middlebox is present. + + In order for CAPWAP to be compatible with potential middleboxes in + the network, CAPWAP implementations MUST send return traffic from the + same port on which it received traffic from a given peer. Further, + any unsolicited requests generated by a CAPWAP node MUST be sent on + the same port. + + Note that this middlebox detection technique is not foolproof. If + the public IP address assigned to the NAT is identical to the private + IP address used by the AC, detection by the WTP would fail. This + failure can lead to various protocol errors, so it is therefore + necessary for deployments to ensure that the NAT's IP address is not + the same as the ACs. + + + +Calhoun, et al. Standards Track [Page 133] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + The CAPWAP protocol allows for all of the AC identities supporting a + group of WTPs to be communicated through the AC List message element. + This feature MUST be ignored by the WTP when it detects the AC is + behind a middlebox. + + The CAPWAP protocol allows an AC to configure a static IP address on + a WTP using the WTP Static IP Address Information message element. + This message element SHOULD NOT be used in NAT'ed environments, + unless the administrator is familiar with the internal IP addressing + scheme within the WTP's private network, and does not rely on the + public address seen by the AC. + + When a WTP detects the duplicate address condition, it generates a + message to the AC, which includes the Duplicate IP Address message + element. The IP address embedded within this message element is + different from the public IP address seen by the AC. + +12. Security Considerations + + This section describes security considerations for the CAPWAP + protocol. It also provides security recommendations for protocols + used in conjunction with CAPWAP. + +12.1. CAPWAP Security + + As it is currently specified, the CAPWAP protocol sits between the + security mechanisms specified by the wireless link layer protocol + (e.g., IEEE 802.11i) and Authentication, Authorization, and + Accounting (AAA). One goal of CAPWAP is to bootstrap trust between + the STA and WTP using a series of preestablished trust relationships: + + STA WTP AC AAA + ============================================== + + DTLS Cred AAA Cred + <------------><-------------> + + EAP Credential + <------------------------------------------> + + wireless link layer + (e.g., 802.11 PTK) + <--------------> or + <---------------------------> + (derived) + + Figure 12: STA Session Setup + + + + +Calhoun, et al. Standards Track [Page 134] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + Within CAPWAP, DTLS is used to secure the link between the WTP and + AC. In addition to securing control messages, it's also a link in + this chain of trust for establishing link layer keys. Consequently, + much rests on the security of DTLS. + + In some CAPWAP deployment scenarios, there are two channels between + the WTP and AC: the control channel, carrying CAPWAP Control + messages, and the data channel, over which client data packets are + tunneled between the AC and WTP. Typically, the control channel is + secured by DTLS, while the data channel is not. + + The use of parallel protected and unprotected channels deserves + special consideration, but does not create a threat. There are two + potential concerns: attempting to convert protected data into + unprotected data and attempting to convert un-protected data into + protected data. These concerns are addressed below. + +12.1.1. Converting Protected Data into Unprotected Data + + Since CAPWAP does not support authentication-only ciphers (i.e., all + supported ciphersuites include encryption and authentication), it is + not possible to convert protected data into unprotected data. Since + encrypted data is (ideally) indistinguishable from random data, the + probability of an encrypted packet passing for a well-formed packet + is effectively zero. + +12.1.2. Converting Unprotected Data into Protected Data (Insertion) + + The use of message authentication makes it impossible for the + attacker to forge protected records. This makes conversion of + unprotected records to protected records impossible. + +12.1.3. Deletion of Protected Records + + An attacker could remove protected records from the stream, though + not undetectably so, due the built-in reliability of the underlying + CAPWAP protocol. In the worst case, the attacker would remove the + same record repeatedly, resulting in a CAPWAP session timeout and + restart. This is effectively a DoS attack, and could be accomplished + by a man in the middle regardless of the CAPWAP protocol security + mechanisms chosen. + +12.1.4. Insertion of Unprotected Records + + An attacker could inject packets into the unprotected channel, but + this may become evident if sequence number desynchronization occurs + as a result. Only if the attacker is a man in the middle (MITM) can + + + + +Calhoun, et al. Standards Track [Page 135] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + packets be inserted undetectably. This is a consequence of that + channel's lack of protection, and not a new threat resulting from the + CAPWAP security mechanism. + +12.1.5. Use of MD5 + + The Image Information message element (Section 4.6.28) makes use of + MD5 to compute the hash field. The authenticity and integrity of the + image file is protected by DTLS, and in this context, MD5 is not used + as a cryptographically secure hash, but just as a basic checksum. + Therefore, the use of MD5 is not considered a security vulnerability, + and no mechanisms for algorithm agility are provided. + +12.1.6. CAPWAP Fragmentation + + RFC 4963 [RFC4963] describes a possible security vulnerability where + a malicious entity can "corrupt" a flow by injecting fragments. By + sending "high" fragments (those with offset greater than zero) with a + forged source address, the attacker can deliberately cause + corruption. The use of DTLS on the CAPWAP Data channel can be used + to avoid this possible vulnerability. + +12.2. Session ID Security + + Since DTLS does not export a unique session identifier, there can be + no explicit protocol binding between the DTLS layer and CAPWAP layer. + As a result, implementations MUST provide a mechanism for performing + this binding. For example, an AC MUST NOT associate decrypted DTLS + control packets with a particular WTP session based solely on the + Session ID in the packet header. Instead, identification should be + done based on which DTLS session decrypted the packet. Otherwise, + one authenticated WTP could spoof another authenticated WTP by + altering the Session ID in the encrypted CAPWAP Header. + + It should be noted that when the CAPWAP Data channel is unencrypted, + the WTP Session ID is exposed and possibly known to adversaries and + other WTPs. This would allow the forgery of the source of data- + channel traffic. This, however, should not be a surprise for + unencrypted data channels. When the data channel is encrypted, the + Session ID is not exposed, and therefore can safely be used to + associate a data and control channel. The 128-bit length of the + Session ID mitigates online guessing attacks where an adversarial, + authenticated WTP tries to correlate his own data channel with + another WTP's control channel. Note that for encrypted data + channels, the Session ID should only be used for correlation for the + first packet immediately after the initial DTLS handshake. Future + correlation should instead be done via identification of a packet's + DTLS session. + + + +Calhoun, et al. Standards Track [Page 136] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +12.3. Discovery or DTLS Setup Attacks + + Since the Discovery Request messages are sent in the clear, it is + important that AC implementations NOT assume that receiving a + Discovery Request message from a WTP implies that the WTP has + rebooted, and consequently tear down any active DTLS sessions. + Discovery Request messages can easily be spoofed by malicious + devices, so it is important that the AC maintain two separate sets of + states for the WTP until the DTLSSessionEstablished notification is + received, indicating that the WTP was authenticated. Once a new DTLS + session is successfully established, any state referring to the old + session can be cleared. + + Similarly, when the AC is entering the DTLS Setup phase, it SHOULD + NOT assume that the WTP has reset, and therefore should not discard + active state until the DTLS session has been successfully + established. While the HelloVerifyRequest provides some protection + against denial-of-service (DoS) attacks on the AC, an adversary + capable of receiving packets at a valid address (or a malfunctioning + or misconfigured WTP) may repeatedly attempt DTLS handshakes with the + AC, potentially creating a resource shortage. If either the + FailedDTLSSessionCount or the FailedDTLSAuthFailCount counter reaches + the value of MaxFailedDTLSSessionRetry variable (see Section 4.8), + implementations MAY choose to rate-limit new DTLS handshakes for some + period of time. It is RECOMMENDED that implementations choosing to + implement rate-limiting use a random discard technique, rather than + mimicking the WTP's sulking behavior. This will ensure that messages + from valid WTPs will have some probability of eliciting a response, + even in the face of a significant DoS attack. + + Some CAPWAP implementations may wish to restrict the DTLS setup + process to only those peers that have been configured in the access + control list, authorizing only those clients to initiate a DTLS + handshake. Note that the impact of this on mitigating denial-of- + service attacks against the DTLS layer is minimal, because DTLS + already uses client-side cookies to minimize processor consumption + attacks. + +12.4. Interference with a DTLS Session + + If a WTP or AC repeatedly receives packets that fail DTLS + authentication or decryption, this could indicate a DTLS + desynchronization between the AC and WTP, a link prone to + undetectable bit errors, or an attacker trying to disrupt a DTLS + session. + + + + + + +Calhoun, et al. Standards Track [Page 137] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + In the state machine (section 2.3), transitions to the DTLS Tear Down + (TD) state can be triggered by frequently receiving DTLS packets with + authentication or decryption errors. The threshold or technique for + deciding when to move to the tear down state should be chosen + carefully. Being able to easily transition to DTLS TD allows easy + detection of malfunctioning devices, but allows for denial-of-service + attacks. Making it difficult to transition to DTLS TD prevents + denial-of-service attacks, but makes it more difficult to detect and + reset a malfunctioning session. Implementers should set this policy + with care. + +12.5. CAPWAP Pre-Provisioning + + In order for CAPWAP to establish a secure communication with a peer, + some level of pre-provisioning on both the WTP and AC is necessary. + This section will detail the minimal number of configuration + parameters. + + When using pre-shared keys, it is necessary to configure the pre- + shared key for each possible peer with which a DTLS session may be + established. To support this mode of operation, one or more entries + of the following table may be configured on either the AC or WTP: + + o Identity: The identity of the peering AC or WTP. This format MAY + be in the form of either an IP address or host name (the latter of + which needs to be resolved to an IP address using DNS). + + o Key: The pre-shared key for use with the peer when establishing + the DTLS session (see Section 12.6 for more information). + + o PSK Identity: Identity hint associated with the provisioned key + (see Section 2.4.4.4 for more information). + + When using certificates, the following items need to be pre- + provisioned: + + o Device Certificate: The local device's certificate (see + Section 12.7 for more information). + + o Trust Anchor: Trusted root certificate chain used to validate any + certificate received from CAPWAP peers. Note that one or more + root certificates MAY be configured on a given device. + + Regardless of the authentication method, the following item needs to + be pre-provisioned: + + + + + + +Calhoun, et al. Standards Track [Page 138] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + o Access Control List: The access control list table contains the + identities of one or more CAPWAP peers, along with a rule. The + rule is used to determine whether communication with the peer is + permitted (see Section 2.4.4.3 for more information). + +12.6. Use of Pre-Shared Keys in CAPWAP + + While use of pre-shared keys may provide deployment and provisioning + advantages not found in public-key-based deployments, it also + introduces a number of operational and security concerns. In + particular, because the keys must typically be entered manually, it + is common for people to base them on memorable words or phrases. + These are referred to as "low entropy passwords/passphrases". + + Use of low-entropy pre-shared keys, coupled with the fact that the + keys are often not frequently updated, tends to significantly + increase exposure. For these reasons, the following recommendations + are made: + + o When DTLS is used with a pre-shared key (PSK) ciphersuite, each + WTP SHOULD have a unique PSK. Since WTPs will likely be widely + deployed, their physical security is not guaranteed. If PSKs are + not unique for each WTP, key reuse would allow the compromise of + one WTP to result in the compromise of others. + + o Generating PSKs from low entropy passwords is NOT RECOMMENDED. + + o It is RECOMMENDED that implementations that allow the + administrator to manually configure the PSK also provide a + capability for generation of new random PSKs, taking RFC 4086 + [RFC4086] into account. + + o Pre-shared keys SHOULD be periodically updated. Implementations + MAY facilitate this by providing an administrative interface for + automatic key generation and periodic update, or it MAY be + accomplished manually instead. + + Every pairwise combination of WTP and AC on the network SHOULD have a + unique PSK. This prevents the domino effect (see "Guidance for + Authentication, Authorization, and Accounting (AAA) Key Management" + [RFC4962]). If PSKs are tied to specific WTPs, then knowledge of the + PSK implies a binding to a specified identity that can be authorized. + + If PSKs are shared, this binding between device and identity is no + longer possible. Compromise of one WTP can yield compromise of + another WTP, violating the CAPWAP security hierarchy. Consequently, + sharing keys between WTPs is NOT RECOMMENDED. + + + + +Calhoun, et al. Standards Track [Page 139] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +12.7. Use of Certificates in CAPWAP + + For public-key-based DTLS deployments, each device SHOULD have unique + credentials, with an extended key usage authorizing the device to act + as either a WTP or AC. If devices do not have unique credentials, it + is possible that by compromising one device, any other device using + the same credential may also be considered to be compromised. + + Certificate validation involves checking a large variety of things. + Since the necessary things to validate are often environment- + specific, many are beyond the scope of this document. In this + section, we provide some basic guidance on certificate validation. + + Each device is responsible for authenticating and authorizing devices + with which they communicate. Authentication entails validation of + the chain of trust leading to the peer certificate, followed by the + peer certificate itself. Implementations SHOULD also provide a + secure method for verifying that the credential in question has not + been revoked. + + Note that if the WTP relies on the AC for network connectivity (e.g., + the AC is a Layer 2 switch to which the WTP is directly connected), + the WTP may not be able to contact an Online Certificate Status + Protocol (OCSP) server or otherwise obtain an up-to-date Certificate + Revocation List (CRL) if a compromised AC doesn't explicitly permit + this. This cannot be avoided, except through effective physical + security and monitoring measures at the AC. + + Proper validation of certificates typically requires checking to + ensure the certificate has not yet expired. If devices have a real- + time clock, they SHOULD verify the certificate validity dates. If no + real-time clock is available, the device SHOULD make a best-effort + attempt to validate the certificate validity dates through other + means. Failure to check a certificate's temporal validity can make a + device vulnerable to man-in-the-middle attacks launched using + compromised, expired certificates, and therefore devices should make + every effort to perform this validation. + +12.8. Use of MAC Address in CN Field + + The CAPWAP protocol is an evolution of an existing protocol [LWAPP], + which is implemented on a large number of already deployed ACs and + WTPs. Every one of these devices has an existing X.509 certificate, + which is provisioned at the time of manufacturing. These X.509 + certificates use the device's MAC address in the Common Name (CN) + field. It is well understood that encoding the MAC address in the CN + field is less than optimal, and using the SubjectAltName field would + be preferable. However, at the time of publication, there is no URN + + + +Calhoun, et al. Standards Track [Page 140] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + specification that allows for the MAC address to be used in the + SubjectAltName field. As such a specification is published by the + IETF, future versions of the CAPWAP protocol MAY require support for + the new URN scheme. + +12.9. AAA Security + + The AAA protocol is used to distribute Extensible Authentication + Protocol (EAP) keys to the ACs, and consequently its security is + important to the overall system security. When used with Transport + Layer Security (TLS) or IPsec, security guidelines specified in RFC + 3539 [RFC3539] SHOULD be followed. + + In general, the link between the AC and AAA server SHOULD be secured + using a strong ciphersuite keyed with mutually authenticated session + keys. Implementations SHOULD NOT rely solely on Basic RADIUS shared + secret authentication as it is often vulnerable to dictionary + attacks, but rather SHOULD use stronger underlying security + mechanisms. + +12.10. WTP Firmware + + The CAPWAP protocol defines a mechanism by which the AC downloads new + firmware to the WTP. During the session establishment process, the + WTP provides information about its current firmware to the AC. The + AC then decides whether the WTP's firmware needs to be updated. It + is important to note that the CAPWAP specification makes the explicit + assumption that the WTP is providing the correct firmware version to + the AC, and is therefore not lying. Further, during the firmware + download process, the CAPWAP protocol does not provide any mechanisms + to recognize whether the WTP is actually storing the firmware for + future use. + +13. Operational Considerations + + The CAPWAP protocol assumes that it is the only configuration + interface to the WTP to configure parameters that are specified in + the CAPWAP specifications. While the use of a separate management + protocol MAY be used for the purposes of monitoring the WTP directly, + configuring the WTP through a separate management interface is not + recommended. Configuring the WTP through a separate protocol, such + as via a command line interface (CLI) or Simple Network Management + Protocol (SNMP), could lead to the AC state being out of sync with + the WTP. + + + + + + + +Calhoun, et al. Standards Track [Page 141] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + The CAPWAP protocol does not deal with the management of the ACs. + The AC is assumed to be configured through some separate management + interface, which could be via a proprietary CLI, SNMP, Network + Configuration Protocol (NETCONF), or some other management protocol. + + The CAPWAP protocol's control channel is fairly lightweight from a + traffic perspective. Once the WTP has been configured, the WTP sends + periodic statistics. Further, the specification calls for a keep- + alive packet to be sent on the protocol's data channel to make sure + that any possible middleboxes (e.g., NAT) maintain their UDP state. + The overhead associated with the control and data channel is not + expected to impact network traffic. That said, the CAPWAP protocol + does allow for the frequency of these packets to be modified through + the DataChannelKeepAlive and StatisticsTimer (see Section 4.7.2 and + Section 4.7.14, respectively). + +14. Transport Considerations + + The CAPWAP WG carefully considered the congestion control + requirements of the CAPWAP protocol, both for the CAPWAP Control and + Data channels. + + CAPWAP specifies a single-threaded command/response protocol to be + used on the control channel, and we have specified that an + exponential back-off algorithm should be used when commands are + retransmitted. When CAPWAP runs in its default mode (Local MAC), the + control channel is the only CAPWAP channel. + + However, CAPWAP can also be run in Split MAC mode, in which case + there will be a DTLS-encrypted data channel between each WTP and the + AC. The WG discussed various options for providing congestion + control on this channel. However, due to performance problems with + TCP when it is run over another congestion control mechanism and the + fact that the vast majority of traffic run over the CAPWAP Data + channel is likely to be congestion-controlled IP traffic, the CAPWAP + WG felt that specifying a congestion control mechanism for the CAPWAP + Data channel would be more likely to cause problems than to resolve + any. + + Because there is no congestion control mechanism specified for the + CAPWAP Data channel, it is RECOMMENDED that non-congestion-controlled + traffic not be tunneled over CAPWAP. When a significant amount of + non-congestion-controlled traffic is expected to be present on a + WLAN, the CAPWAP connection between the AC and the WTP for that LAN + should be configured to remain in Local MAC mode with Distribution + function at the WTP. + + + + + +Calhoun, et al. Standards Track [Page 142] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + The lock step nature of the CAPWAP protocol's control channel can + cause the firmware download process to take some time, depending upon + the round-trip time (RTT). This is not expected to be a problem + since the CAPWAP protocol allows firmware to be downloaded while the + WTP provides service to wireless clients/devices. + + It is necessary for the WTP and AC to configure their MTU based on + the capabilities of the path. See Section 3.5 for more information. + + The CAPWAP protocol mandates support of the Explicit Congestion + Notification (ECN) through a mode of operation named "limited + functionality option", detailed in section 9.1.1 of [RFC3168]. + Future versions of the CAPWAP protocol should consider mandating + support for the "full functionality option". + +15. IANA Considerations + + This section details the actions that IANA has taken in preparation + for publication of the specification. Numerous registries have been + created, and the contents, document action (see [RFC5226], and + registry format are all included below. Note that in cases where bit + fields are referred to, the bit numbering is left to right, where the + leftmost bit is labeled as bit zero (0). + + For future registration requests where an Expert Review is required, + a Designated Expert should be consulted, which is appointed by the + responsible IESG Area Director. The intention is that any allocation + will be accompanied by a published RFC, but given that other SDOs may + want to create standards built on top of CAPWAP, a document the + Designated Expert can review is also acceptable. IANA should allow + for allocation of values prior to documents being approved for + publication, so the Designated Expert can approve allocations once it + seems clear that publication will occur. The Designated Expert will + post a request to the CAPWAP WG mailing list (or a successor + designated by the Area Director) for comment and review. Before a + period of 30 days has passed, the Designated Expert will either + approve or deny the registration request and publish a notice of the + decision to the CAPWAP WG mailing list or its successor, as well as + informing IANA. A denial notice must be justified by an explanation, + and in the cases where it is possible, concrete suggestions on how + the request can be modified so as to become acceptable should be + provided. + +15.1. IPv4 Multicast Address + + IANA has registered a new IPv4 multicast address called "capwap-ac" + from the Internetwork Control Block IPv4 multicast address registry; + see Section 3.3. + + + +Calhoun, et al. Standards Track [Page 143] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +15.2. IPv6 Multicast Address + + IANA has registered a new organization local multicast address called + the "All ACs multicast address" in the Variable Scope IPv6 multicast + address registry; see Section 3.3. + +15.3. UDP Port + + IANA registered two new UDP Ports, which are organization-local + multicast addresses, in the registered port numbers registry; see + Section 3.1. The following values have been registered: + + Keyword Decimal Description References + ------- ------- ----------- ---------- + capwap-control 5246/udp CAPWAP Control Protocol This Document + capwap-data 5247/udp CAPWAP Data Protocol This Document + + +15.4. CAPWAP Message Types + + The Message Type field in the CAPWAP Header (see Section 4.5.1.1) is + used to identify the operation performed by the message. There are + multiple namespaces, which are identified via the first three octets + of the field containing the IANA Enterprise Number [RFC5226]. + + IANA maintains the CAPWAP Message Types registry for all message + types whose Enterprise Number is set to zero (0). The namespace is 8 + bits (0-255), where the value of zero (0) is reserved and must not be + assigned. The values one (1) through 26 are allocated in this + specification, and can be found in Section 4.5.1.1. Any new + assignments of a CAPWAP Message Type whose Enterprise Number is set + to zero (0) requires an Expert Review. The registry maintained by + IANA has the following format: + + CAPWAP Control Message Message Type Reference + Value + +15.5. CAPWAP Header Flags + + The Flags field in the CAPWAP Header (see Section 4.3) is 9 bits in + length and is used to identify any special treatment related to the + message. This specification defines bits zero (0) through five (5), + while bits six (6) through eight (8) are reserved. There are + currently three unused, reserved bits that are managed by IANA and + whose assignment require an Expert Review. IANA created the CAPWAP + Header Flags registry, whose format is: + + Flag Field Name Bit Position Reference + + + +Calhoun, et al. Standards Track [Page 144] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +15.6. CAPWAP Control Message Flags + + The Flags field in the CAPWAP Control Message header (see + Section 4.5.1.4) is used to identify any special treatment related to + the control message. There are currently eight (8) unused, reserved + bits. The assignment of these bits is managed by IANA and requires + an Expert Review. IANA created the CAPWAP Control Message Flags + registry, whose format is: + + Flag Field Name Bit Position Reference + +15.7. CAPWAP Message Element Type + + The Type field in the CAPWAP Message Element header (see Section 4.6) + is used to identify the data being transported. The namespace is 16 + bits (0-65535), where the value of zero (0) is reserved and must not + be assigned. The values one (1) through 53 are allocated in this + specification, and can be found in Section 4.5.1.1. + + The 16-bit namespace is further divided into blocks of addresses that + are reserved for specific CAPWAP wireless bindings. The following + blocks are reserved: + + CAPWAP Protocol Message Elements 1 - 1023 + IEEE 802.11 Message Elements 1024 - 2047 + EPCGlobal Message Elements 3072 - 4095 + + This namespace is managed by IANA and assignments require an Expert + Review. IANA created the CAPWAP Message Element Type registry, whose + format is: + + CAPWAP Message Element Type Value Reference + +15.8. CAPWAP Wireless Binding Identifiers + + The Wireless Binding Identifier (WBID) field in the CAPWAP Header + (see Section 4.3) is used to identify the wireless technology + associated with the packet. This specification allocates the values + one (1) and three (3). Due to the limited address space available, a + new WBID request requires Expert Review. IANA created the CAPWAP + Wireless Binding Identifier registry, whose format is: + + CAPWAP Wireless Binding Identifier Type Value Reference + + + + + + + + +Calhoun, et al. Standards Track [Page 145] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +15.9. AC Security Types + + The Security field in the AC Descriptor message element (see + Section 4.6.1) is 8 bits in length and is used to identify the + authentication methods available on the AC. This specification + defines bits five (5) and six (6), while bits zero (0) through four + (4) as well as bit seven (7) are reserved and unused. These reserved + bits are managed by IANA and assignment requires Standards Action. + IANA created the AC Security Types registry, whose format is: + + AC Security Type Bit Position Reference + +15.10. AC DTLS Policy + + The DTLS Policy field in the AC Descriptor message element (see + Section 4.6.1) is 8 bits in length and is used to identify whether + the CAPWAP Data Channel is to be secured. This specification defines + bits five (5) and six (6), while bits zero (0) through four (4) as + well as bit seven (7) are reserved and unused. These reserved bits + are managed by IANA and assignment requires Standards Action. IANA + created the AC DTLS Policy registry, whose format is: + + AC DTLS Policy Bit Position Reference + +15.11. AC Information Type + + The Information Type field in the AC Descriptor message element (see + Section 4.6.1) is used to represent information about the AC. The + namespace is 16 bits (0-65535), where the value of zero (0) is + reserved and must not be assigned. This field, combined with the AC + Information Vendor ID, allows vendors to use a private namespace. + This specification defines the AC Information Type namespace when the + AC Information Vendor ID is set to zero (0), for which the values + four (4) and five (5) are allocated in this specification, and can be + found in Section 4.6.1. This namespace is managed by IANA and + assignments require an Expert Review. IANA created the AC + Information Type registry, whose format is: + + AC Information Type Type Value Reference + +15.12. CAPWAP Transport Protocol Types + + The Transport field in the CAPWAP Transport Protocol message element + (see Section 4.6.14) is used to identify the transport to use for the + CAPWAP Data Channel. The namespace is 8 bits (0-255), where the + value of zero (0) is reserved and must not be assigned. The values + one (1) and two (2) are allocated in this specification, and can be + + + + +Calhoun, et al. Standards Track [Page 146] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + found in Section 4.6.14. This namespace is managed by IANA and + assignments require an Expert Review. IANA created the CAPWAP + Transport Protocol Types registry, whose format is: + + CAPWAP Transport Protocol Type Type Value Reference + +15.13. Data Transfer Type + + The Data Type field in the Data Transfer Data message element (see + Section 4.6.15) and Image Data message element (see Section 4.6.26) + is used to provide information about the data being carried. The + namespace is 8 bits (0-255), where the value of zero (0) is reserved + and must not be assigned. The values one (1), two (2), and five (5) + are allocated in this specification, and can be found in + Section 4.6.15. This namespace is managed by IANA and assignments + require an Expert Review. IANA created the Data Transfer Type + registry, whose format is: + + Data Transfer Type Type Value Reference + +15.14. Data Transfer Mode + + The Data Mode field in the Data Transfer Data message element (see + Section 4.6.15) and Data Transfer Mode message element (see + Section 15.14) is used to provide information about the data being + carried. The namespace is 8 bits (0-255), where the value of zero + (0) is reserved and must not be assigned. The values one (1) and two + (2) are allocated in this specification, and can be found in + Section 15.14. This namespace is managed by IANA and assignments + require an Expert Review. IANA created the Data Transfer Mode + registry, whose format is: + + Data Transfer Mode Type Value Reference + +15.15. Discovery Types + + The Discovery Type field in the Discovery Type message element (see + Section 4.6.21) is used by the WTP to indicate to the AC how it was + discovered. The namespace is 8 bits (0-255). The values zero (0) + through four (4) are allocated in this specification and can be found + in Section 4.6.21. This namespace is managed by IANA and assignments + require an Expert Review. IANA created the Discovery Types registry, + whose format is: + + Discovery Types Type Value Reference + + + + + + +Calhoun, et al. Standards Track [Page 147] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +15.16. ECN Support + + The ECN Support field in the ECN Support message element (see + Section 4.6.25) is used by the WTP to represent its ECN Support. The + namespace is 8 bits (0-255). The values zero (0) and one (1) are + allocated in this specification, and can be found in Section 4.6.25. + This namespace is managed by IANA and assignments require an Expert + Review. IANA created the ECN Support registry, whose format is: + + ECN Support Type Value Reference + +15.17. Radio Admin State + + The Radio Admin field in the Radio Administrative State message + element (see Section 4.6.33) is used by the WTP to represent the + state of its radios. The namespace is 8 bits (0-255), where the + value of zero (0) is reserved and must not be assigned. The values + one (1) and two (2) are allocated in this specification, and can be + found in Section 4.6.33. This namespace is managed by IANA and + assignments require an Expert Review. IANA created the Radio Admin + State registry, whose format is: + + Radio Admin State Type Value Reference + +15.18. Radio Operational State + + The State field in the Radio Operational State message element (see + Section 4.6.34) is used by the WTP to represent the operational state + of its radios. The namespace is 8 bits (0-255), where the value of + zero (0) is reserved and must not be assigned. The values one (1) + and two (2) are allocated in this specification, and can be found in + Section 4.6.34. This namespace is managed by IANA and assignments + require an Expert Review. IANA created the Radio Operational State + registry, whose format is: + + Radio Operational State Type Value Reference + +15.19. Radio Failure Causes + + The Cause field in the Radio Operational State message element (see + Section 4.6.34) is used by the WTP to represent the reason a radio + may have failed. The namespace is 8 bits (0-255), where the value of + zero (0) through three (3) are allocated in this specification, and + can be found in Section 4.6.34. This namespace is managed by IANA + and assignments require an Expert Review. IANA created the Radio + Failure Causes registry, whose format is: + + Radio Failure Causes Type Value Reference + + + +Calhoun, et al. Standards Track [Page 148] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +15.20. Result Code + + The Result Code field in the Result Code message element (see + Section 4.6.35) is used to indicate the success or failure of a + CAPWAP Control message. The namespace is 32 bits (0-4294967295), + where the value of zero (0) through 22 are allocated in this + specification, and can be found in Section 4.6.35. This namespace is + managed by IANA and assignments require an Expert Review. IANA + created the Result Code registry, whose format is: + + Result Code Type Value Reference + +15.21. Returned Message Element Reason + + The Reason field in the Returned Message Element message element (see + Section 4.6.36) is used to indicate the reason why a message element + was not processed successfully. The namespace is 8 bits (0-255), + where the value of zero (0) is reserved and must not be assigned. + The values one (1) through four (4) are allocated in this + specification, and can be found in Section 4.6.36. This namespace is + managed by IANA and assignments require an Expert Review. IANA + created the Returned Message Element Reason registry, whose format + is: + + Returned Message Element Reason Type Value Reference + +15.22. WTP Board Data Type + + The Board Data Type field in the WTP Board Data message element (see + Section 4.6.40) is used to represent information about the WTP + hardware. The namespace is 16 bits (0-65535). The WTP Board Data + Type values zero (0) through four (4) are allocated in this + specification, and can be found in Section 4.6.40. This namespace is + managed by IANA and assignments require an Expert Review. IANA + created the WTP Board Data Type registry, whose format is: + + WTP Board Data Type Type Value Reference + +15.23. WTP Descriptor Type + + The Descriptor Type field in the WTP Descriptor message element (see + Section 4.6.41) is used to represent information about the WTP + software. The namespace is 16 bits (0-65535). This field, combined + with the Descriptor Vendor ID, allows vendors to use a private + namespace. This specification defines the WTP Descriptor Type + namespace when the Descriptor Vendor ID is set to zero (0), for which + the values zero (0) through three (3) are allocated in this + + + + +Calhoun, et al. Standards Track [Page 149] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + specification, and can be found in Section 4.6.41. This namespace is + managed by IANA and assignments require an Expert Review. IANA + created the WTP Board Data Type registry, whose format is: + + WTP Descriptor Type Type Value Reference + +15.24. WTP Fallback Mode + + The Mode field in the WTP Fallback message element (see + Section 4.6.42) is used to indicate the type of AC fallback mechanism + the WTP should employ. The namespace is 8 bits (0-255), where the + value of zero (0) is reserved and must not be assigned. The values + one (1) and two (2) are allocated in this specification, and can be + found in Section 4.6.42. This namespace is managed by IANA and + assignments require an Expert Review. IANA created the WTP Fallback + Mode registry, whose format is: + + WTP Fallback Mode Type Value Reference + +15.25. WTP Frame Tunnel Mode + + The Tunnel Type field in the WTP Frame Tunnel Mode message element + (see Section 4.6.43) is 8 bits and is used to indicate the type of + tunneling to use between the WTP and the AC. This specification + defines bits four (4) through six (6), while bits zero (0) through + three (3) as well as bit seven (7) are reserved and unused. These + reserved bits are managed by IANA and assignment requires an Expert + Review. IANA created the WTP Frame Tunnel Mode registry, whose + format is: + + WTP Frame Tunnel Mode Bit Position Reference + +15.26. WTP MAC Type + + The MAC Type field in the WTP MAC Type message element (see + Section 4.6.44) is used to indicate the type of MAC to use in + tunneled frames between the WTP and the AC. The namespace is 8 bits + (0-255), where the value of zero (0) through two (2) are allocated in + this specification, and can be found in Section 4.6.44. This + namespace is managed by IANA and assignments require an Expert + Review. IANA created the WTP MAC Type registry, whose format is: + + WTP MAC Type Type Value Reference + + + + + + + + +Calhoun, et al. Standards Track [Page 150] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +15.27. WTP Radio Stats Failure Type + + The Last Failure Type field in the WTP Radio Statistics message + element (see Section 4.6.46) is used to indicate the last WTP + failure. The namespace is 8 bits (0-255), where the value of zero + (0) through three (3) as well as the value 255 are allocated in this + specification, and can be found in Section 4.6.46. This namespace is + managed by IANA and assignments require an Expert Review. IANA + created the WTP Radio Stats Failure Type registry, whose format is: + + WTP Radio Stats Failure Type Type Value Reference + +15.28. WTP Reboot Stats Failure Type + + The Last Failure Type field in the WTP Reboot Statistics message + element (see Section 4.6.47) is used to indicate the last reboot + reason. The namespace is 8 bits (0-255), where the value of zero (0) + through five (5) as well as the value 255 are allocated in this + specification, and can be found in Section 4.6.47. This namespace is + managed by IANA and assignments require an Expert Review. IANA + created the WTP Reboot Stats Failure Type registry, whose format is: + + WTP Reboot Stats Failure Type Type Value Reference + +16. Acknowledgments + + The following individuals are acknowledged for their contributions to + this protocol specification: Puneet Agarwal, Abhijit Choudhury, Pasi + Eronen, Saravanan Govindan, Peter Nilsson, David Perkins, and Yong + Zhang. + + Michael Vakulenko contributed text to describe how CAPWAP can be used + over Layer 3 (IP/UDP) networks. + +17. References + +17.1. Normative References + + [RFC1191] Mogul, J. and S. Deering, "Path MTU discovery", + RFC 1191, November 1990. + + [RFC1321] Rivest, R., "The MD5 Message-Digest Algorithm", + RFC 1321, April 1992. + + [RFC1305] Mills, D., "Network Time Protocol (Version 3) + Specification, Implementation", RFC 1305, + March 1992. + + + + +Calhoun, et al. Standards Track [Page 151] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + [RFC1981] McCann, J., Deering, S., and J. Mogul, "Path MTU + Discovery for IP version 6", RFC 1981, + August 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to + Indicate Requirement Levels", BCP 14, RFC 2119, + March 1997. + + [RFC2460] Deering, S. and R. Hinden, "Internet Protocol, + Version 6 (IPv6) Specification", RFC 2460, + December 1998. + + [RFC2474] Nichols, K., Blake, S., Baker, F., and D. Black, + "Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers", + RFC 2474, December 1998. + + [RFC2782] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS + RR for specifying the location of services (DNS + SRV)", RFC 2782, February 2000. + + [RFC3168] Ramakrishnan, K., Floyd, S., and D. Black, "The + Addition of Explicit Congestion Notification (ECN) + to IP", RFC 3168, September 2001. + + [RFC3539] Aboba, B. and J. Wood, "Authentication, + Authorization and Accounting (AAA) Transport + Profile", RFC 3539, June 2003. + + [RFC3629] Yergeau, F., "UTF-8, a transformation format of + ISO 10646", STD 63, RFC 3629, November 2003. + + [RFC3828] Larzon, L-A., Degermark, M., Pink, S., Jonsson, + L-E., and G. Fairhurst, "The Lightweight User + Datagram Protocol (UDP-Lite)", RFC 3828, + July 2004. + + [RFC4086] Eastlake, D., Schiller, J., and S. Crocker, + "Randomness Requirements for Security", BCP 106, + RFC 4086, June 2005. + + [RFC4279] Eronen, P. and H. Tschofenig, "Pre-Shared Key + Ciphersuites for Transport Layer Security (TLS)", + RFC 4279, December 2005. + + [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer + Security (TLS) Protocol Version 1.2", RFC 5246, + August 2008. + + + +Calhoun, et al. Standards Track [Page 152] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + [RFC4347] Rescorla, E. and N. Modadugu, "Datagram Transport + Layer Security", RFC 4347, April 2006. + + [RFC4821] Mathis, M. and J. Heffner, "Packetization Layer + Path MTU Discovery", RFC 4821, March 2007. + + [RFC4963] Heffner, J., Mathis, M., and B. Chandler, "IPv4 + Reassembly Errors at High Data Rates", RFC 4963, + July 2007. + + [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for + Writing an IANA Considerations Section in RFCs", + BCP 26, RFC 5226, May 2008. + + [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, + S., Housley, R., and W. Polk, "Internet X.509 + Public Key Infrastructure Certificate and + Certificate Revocation List (CRL) Profile", + RFC 5280, May 2008. + + [ISO.9834-1.1993] International Organization for Standardization, + "Procedures for the operation of OSI registration + authorities - part 1: general procedures", + ISO Standard 9834-1, 1993. + + [RFC5416] Calhoun, P., Ed., Montemurro, M., Ed., and D. + Stanley, Ed., "Control And Provisioning of + Wireless Access Points (CAPWAP) Protocol Binding + for IEEE 802.11", RFC 5416, March 2009. + + [RFC5417] Calhoun, P., "Control And Provisioning of Wireless + Access Points (CAPWAP) Access Controller DHCP + Option", RFC 5417, March 2009. + + [FRAME-EXT] IEEE, "IEEE Standard 802.3as-2006", 2005. + +17.2. Informative References + + [RFC3232] Reynolds, J., "Assigned Numbers: RFC 1700 is + Replaced by an On-line Database", RFC 3232, + January 2002. + + [RFC3753] Manner, J. and M. Kojo, "Mobility Related + Terminology", RFC 3753, June 2004. + + + + + + + +Calhoun, et al. Standards Track [Page 153] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + + [RFC4564] Govindan, S., Cheng, H., Yao, ZH., Zhou, WH., and + L. Yang, "Objectives for Control and Provisioning + of Wireless Access Points (CAPWAP)", RFC 4564, + July 2006. + + [RFC4962] Housley, R. and B. Aboba, "Guidance for + Authentication, Authorization, and Accounting + (AAA) Key Management", BCP 132, RFC 4962, + July 2007. + + [LWAPP] Calhoun, P., O'Hara, B., Suri, R., Cam Winget, N., + Kelly, S., Williams, M., and S. Hares, + "Lightweight Access Point Protocol", Work in + Progress, March 2007. + + [SLAPP] Narasimhan, P., Harkins, D., and S. Ponnuswamy, + "SLAPP: Secure Light Access Point Protocol", Work + in Progress, May 2005. + + [DTLS-DESIGN] Modadugu, et al., N., "The Design and + Implementation of Datagram TLS", Feb 2004. + + [EUI-48] IEEE, "Guidelines for use of a 48-bit Extended + Unique Identifier", Dec 2005. + + [EUI-64] IEEE, "GUIDELINES FOR 64-BIT GLOBAL IDENTIFIER + (EUI-64) REGISTRATION AUTHORITY". + + [EPCGlobal] "See http://www.epcglobalinc.org/home". + + [PacketCable] "PacketCable Security Specification PKT-SP-SEC- + I12-050812", August 2005, . + + [CableLabs] "OpenCable System Security Specification OC-SP- + SEC-I07-061031", October 2006, . + + [WiMAX] "WiMAX Forum X.509 Device Certificate Profile + Approved Specification V1.0.1", April 2008, + . + + [RFC5418] Kelly, S. and C. Clancy, "Control And Provisioning + for Wireless Access Points (CAPWAP) Threat + Analysis for IEEE 802.11 Deployments", RFC 5418, + March 2009. + + + + + + + +Calhoun, et al. Standards Track [Page 154] + +RFC 5415 CAPWAP Protocol Specification March 2009 + + +Editors' Addresses + + Pat R. Calhoun (editor) + Cisco Systems, Inc. + 170 West Tasman Drive + San Jose, CA 95134 + + Phone: +1 408-902-3240 + EMail: pcalhoun@cisco.com + + Michael P. Montemurro (editor) + Research In Motion + 5090 Commerce Blvd + Mississauga, ON L4W 5M4 + Canada + + Phone: +1 905-629-4746 x4999 + EMail: mmontemurro@rim.com + + + Dorothy Stanley (editor) + Aruba Networks + 1322 Crossman Ave + Sunnyvale, CA 94089 + + Phone: +1 630-363-1389 + EMail: dstanley@arubanetworks.com + + + + + + + + + + + + + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 155] + diff --git a/docs/rfc5416.txt b/docs/rfc5416.txt new file mode 100644 index 0000000..4c0829c --- /dev/null +++ b/docs/rfc5416.txt @@ -0,0 +1,4259 @@ + + + + + + +Network Working Group P. Calhoun, Ed. +Request for Comments: 5416 Cisco Systems, Inc. +Category: Standards Track M. Montemurro, Ed. + Research In Motion + D. Stanley, Ed. + Aruba Networks + March 2009 + + + Control and Provisioning of Wireless Access Points (CAPWAP) Protocol + Binding for IEEE 802.11 + +Status of This Memo + + This document specifies an Internet standards track protocol for the + Internet community, and requests discussion and suggestions for + improvements. Please refer to the current edition of the "Internet + Official Protocol Standards" (STD 1) for the standardization state + and status of this protocol. Distribution of this memo is unlimited. + +Copyright Notice + + Copyright (c) 2009 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents in effect on the date of + publication of this document (http://trustee.ietf.org/license-info). + Please review these documents carefully, as they describe your rights + and restrictions with respect to this document. + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + + + + + + + + + +Calhoun, et al. Standards Track [Page 1] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + +Abstract + + Wireless LAN product architectures have evolved from single + autonomous access points to systems consisting of a centralized + Access Controller (AC) and Wireless Termination Points (WTPs). The + general goal of centralized control architectures is to move access + control, including user authentication and authorization, mobility + management, and radio management from the single access point to a + centralized controller. + + This specification defines the Control And Provisioning of Wireless + Access Points (CAPWAP) Protocol Binding Specification for use with + the IEEE 802.11 Wireless Local Area Network protocol. + +Table of Contents + + 1. Introduction ....................................................4 + 1.1. Goals ......................................................5 + 1.2. Conventions Used in This Document ..........................5 + 1.3. Terminology ................................................5 + 2. IEEE 802.11 Binding .............................................7 + 2.1. CAPWAP Wireless Binding Identifier .........................7 + 2.2. Split MAC and Local MAC Functionality ......................7 + 2.2.1. Split MAC ...........................................7 + 2.2.2. Local MAC ..........................................12 + 2.3. Roaming Behavior ..........................................15 + 2.4. Group Key Refresh .........................................16 + 2.5. BSSID to WLAN ID Mapping ..................................17 + 2.6. CAPWAP Data Channel QoS Behavior ..........................18 + 2.6.1. IEEE 802.11 Data Frames ............................18 + 2.6.1.1. 802.1p Support ............................19 + 2.6.1.2. DSCP Support ..............................19 + 2.6.2. IEEE 802.11 MAC Management Messages ................21 + 2.7. Run State Operation .......................................21 + 3. IEEE 802.11 Specific CAPWAP Control Messages ...................21 + 3.1. IEEE 802.11 WLAN Configuration Request ....................22 + 3.2. IEEE 802.11 WLAN Configuration Response ...................23 + 4. CAPWAP Data Message Bindings ...................................23 + 5. CAPWAP Control Message Bindings ................................25 + 5.1. Discovery Request Message .................................25 + 5.2. Discovery Response Message ................................25 + 5.3. Primary Discovery Request Message .........................25 + 5.4. Primary Discovery Response Message ........................26 + 5.5. Join Request Message ......................................26 + 5.6. Join Response Message .....................................26 + 5.7. Configuration Status Request Message ......................26 + 5.8. Configuration Status Response Message .....................27 + 5.9. Configuration Update Request Message ......................27 + + + +Calhoun, et al. Standards Track [Page 2] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + 5.10. Station Configuration Request ............................28 + 5.11. Change State Event Request ...............................28 + 5.12. WTP Event Request ........................................28 + 6. IEEE 802.11 Message Element Definitions ........................29 + 6.1. IEEE 802.11 Add WLAN ......................................29 + 6.2. IEEE 802.11 Antenna .......................................35 + 6.3. IEEE 802.11 Assigned WTP BSSID ............................36 + 6.4. IEEE 802.11 Delete WLAN ...................................37 + 6.5. IEEE 802.11 Direct Sequence Control .......................37 + 6.6. IEEE 802.11 Information Element ...........................38 + 6.7. IEEE 802.11 MAC Operation .................................39 + 6.8. IEEE 802.11 MIC Countermeasures ...........................41 + 6.9. IEEE 802.11 Multi-Domain Capability .......................42 + 6.10. IEEE 802.11 OFDM Control .................................43 + 6.11. IEEE 802.11 Rate Set .....................................44 + 6.12. IEEE 802.11 RSNA Error Report From Station ...............44 + 6.13. IEEE 802.11 Station ......................................46 + 6.14. IEEE 802.11 Station QoS Profile ..........................47 + 6.15. IEEE 802.11 Station Session Key ..........................48 + 6.16. IEEE 802.11 Statistics ...................................50 + 6.17. IEEE 802.11 Supported Rates ..............................54 + 6.18. IEEE 802.11 Tx Power .....................................54 + 6.19. IEEE 802.11 Tx Power Level ...............................55 + 6.20. IEEE 802.11 Update Station QoS ...........................56 + 6.21. IEEE 802.11 Update WLAN ..................................57 + 6.22. IEEE 802.11 WTP Quality of Service .......................61 + 6.23. IEEE 802.11 WTP Radio Configuration ......................63 + 6.24. IEEE 802.11 WTP Radio Fail Alarm Indication ..............65 + 6.25. IEEE 802.11 WTP Radio Information ........................66 + 7. IEEE 802.11 Binding WTP Saved Variables ........................67 + 7.1. IEEE80211AntennaInfo ......................................67 + 7.2. IEEE80211DSControl ........................................67 + 7.3. IEEE80211MACOperation .....................................67 + 7.4. IEEE80211OFDMControl ......................................67 + 7.5. IEEE80211Rateset ..........................................67 + 7.6. IEEE80211TxPower ..........................................67 + 7.7. IEEE80211QoS ..............................................68 + 7.8. IEEE80211RadioConfig ......................................68 + 8. Technology Specific Message Element Values .....................68 + 8.1. WTP Descriptor Message Element, Encryption + Capabilities Field ........................................68 + 9. Security Considerations ........................................68 + 9.1. IEEE 802.11 Security ......................................68 + 10. IANA Considerations ...........................................70 + 10.1. CAPWAP Wireless Binding Identifier .......................70 + 10.2. CAPWAP IEEE 802.11 Message Types .........................70 + 10.3. CAPWAP Message Element Type ..............................70 + 10.4. IEEE 802.11 Key Status ...................................71 + + + +Calhoun, et al. Standards Track [Page 3] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + 10.5. IEEE 802.11 QoS ..........................................71 + 10.6. IEEE 802.11 Auth Type ....................................71 + 10.7. IEEE 802.11 Antenna Combiner .............................71 + 10.8. IEEE 802.11 Antenna Selection ............................72 + 10.9. IEEE 802.11 Session Key Flags ............................72 + 10.10. IEEE 802.11 Tagging Policy ..............................72 + 10.11. IEEE 802.11 WTP Radio Fail ..............................72 + 10.12. IEEE 802.11 WTP Radio Type ..............................73 + 10.13. WTP Encryption Capabilities .............................73 + 11. Acknowledgments ...............................................73 + 12. References ....................................................73 + 12.1. Normative References .....................................73 + 12.2. Informative References ...................................75 + +1. Introduction + + The CAPWAP protocol [RFC5415] defines an extensible protocol to allow + an Access Controller to manage wireless agnostic Wireless Termination + Points. The CAPWAP protocol itself does not include any specific + wireless technologies; instead, it relies on a binding specification + to extend the technology to a particular wireless technology. + + This specification defines the Control And Provisioning of Wireless + Access Points (CAPWAP) Protocol Binding Specification for use with + the IEEE 802.11 Wireless Local Area Network protocol. Use of CAPWAP + control message fields, new control messages, and message elements + are defined. The minimum required definitions for a binding-specific + Statistics message element, Station message element, and WTP Radio + Information message element are included. + + Note that this binding only supports the IEEE 802.11-2007 + specification. Of note, this binding does not support the ad hoc + network mode defined in the IEEE 802.11-2007 standard. This + specification also does not cover the use of data frames with the + four-address format, commonly referred to as Wireless Bridges, whose + use is not specified in the IEEE 802.11-2007 standard. This protocol + specification does not currently officially support IEEE 802.11n. + That said, the protocol does allow a WTP to advertise support for an + IEEE 802.11n radio; however, the protocol does not allow for any of + the protocol's additional features to be configured and/or used. New + IEEE protocol specifications published outside of this document + (e.g., IEEE 802.11v, IEEE 802.11r) are also not supported through + this binding, and in addition to IEEE 802.11n, must be addressed + either through a separate CAPWAP binding, or an update to this + binding. + + + + + + +Calhoun, et al. Standards Track [Page 4] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + In order to address immediate market needs for standards still being + developed by the IEEE 802.11 standards body, the WiFi Alliance + created interim pseudo-standards specifications. Two such + specifications are widely used in the industry, namely the WiFi + Protect Access [WPA] and the WiFi MultiMedia [WMM] specifications. + Given their widespread adoption, this CAPWAP binding requires the use + of these two specifications. + +1.1. Goals + + The goals of this CAPWAP protocol binding are to make the + capabilities of the CAPWAP protocol available for use in conjunction + with IEEE 802.11 wireless networks. The capabilities to be made + available can be summarized as: + + 1. To centralize the authentication and policy enforcement functions + for an IEEE 802.11 wireless network. The AC may also provide + centralized bridging, forwarding, and encryption of user traffic. + Centralization of these functions will enable reduced cost and + higher efficiency by applying the capabilities of network + processing silicon to the wireless network, as in wired LANs. + + 2. To enable shifting of the higher-level protocol processing from + the WTP. This leaves the time-critical applications of wireless + control and access in the WTP, making efficient use of the + computing power available in WTPs that are subject to severe cost + pressure. + + The CAPWAP protocol binding extensions defined herein apply solely to + the interface between the WTP and the AC. Inter-AC and station-to-AC + communication are strictly outside the scope of this document. + +1.2. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in RFC 2119 [RFC2119]. + +1.3. Terminology + + This section contains definitions for terms used frequently + throughout this document. However, many additional definitions can + be found in [IEEE.802-11.2007]. + + Access Controller (AC): The network entity that provides WTP access + to the network infrastructure in the data plane, control plane, + management plane, or a combination therein. + + + + +Calhoun, et al. Standards Track [Page 5] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Basic Service Set (BSS): A set of stations controlled by a single + coordination function. + + Distribution: The service that, by using association information, + delivers medium access control (MAC) service data units (MSDUs) + within the distribution system (DS). + + Distribution System Service (DSS): The set of services provided by + the distribution system (DS) that enable the medium access control + (MAC) layer to transport MAC service data units (MSDUs) between + stations that are not in direct communication with each other over a + single instance of the wireless medium (WM). These services include + the transport of MSDUs between the access points (APs) of basic + service sets (BSSs) within an extended service set (ESS), transport + of MSDUs between portals and BSSs within an ESS, and transport of + MSDUs between stations in the same BSS in cases where the MSDU has a + multicast or broadcast destination address, or where the destination + is an individual address but the station sending the MSDU chooses to + involve the DSS. DSSs are provided between pairs of IEEE 802.11 + MACs. + + Integration: The service that enables delivery of medium access + control (MAC) service data units (MSDUs) between the distribution + system (DS) and an existing, non-IEEE 802.11 local area network (via + a portal). + + Station (STA): A device that contains an IEEE 802.11 conformant + medium access control (MAC) and physical layer (PHY) interface to the + wireless medium (WM). + + Portal: The logical point at which medium access control (MAC) + service data units (MSDUs) from a non-IEEE 802.11 local area network + (LAN) enter the distribution system (DS) of an extended service set + (ESS). + + WLAN: In this document, WLAN refers to a logical component + instantiated on a WTP device. A single physical WTP may operate a + number of WLANs. Each Basic Service Set Identifier (BSSID) and its + constituent wireless terminal radios is denoted as a distinct WLAN on + a physical WTP. + + Wireless Termination Point (WTP): The physical or network entity that + contains an IEEE 802.11 RF antenna and wireless PHY to transmit and + receive station traffic for wireless access networks. + + + + + + + +Calhoun, et al. Standards Track [Page 6] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + +2. IEEE 802.11 Binding + + This section describes use of the CAPWAP protocol with the IEEE + 802.11 Wireless Local Area Network protocol, including Local and + Split MAC operation, Group Key Refresh, Basic Service Set + Identification (BSSID) to WLAN Mapping, IEEE 802.11 MAC management + frame Quality of Service (Qos) tagging and Run State operation. + +2.1. CAPWAP Wireless Binding Identifier + + The CAPWAP Header, defined in Section 4.3 of [RFC5415] requires that + all CAPWAP binding specifications have a Wireless Binding Identifier + (WBID) assigned. This document, which defines the IEEE 802.11 + binding, uses the value one (1). + +2.2. Split MAC and Local MAC Functionality + + The CAPWAP protocol, when used with IEEE 802.11 devices, requires + specific behavior from the WTP and the AC to support the required + IEEE 802.11 protocol functions. + + For both the Split and Local MAC approaches, the CAPWAP functions, as + defined in the taxonomy specification [RFC4118], reside in the AC. + + To provide system component interoperability, the WTP and AC MUST + support 802.11 encryption/decryption at the WTP. The WTP and AC MAY + support 802.11 encryption/decryption at the AC. + +2.2.1. Split MAC + + This section shows the division of labor between the WTP and the AC + in a Split MAC architecture. Figure 1 shows the separation of + functionality between CAPWAP components. + + + + + + + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 7] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Function Location + Distribution Service AC + Integration Service AC + Beacon Generation WTP + Probe Response Generation WTP + Power Mgmt/Packet Buffering WTP + Fragmentation/Defragmentation WTP/AC + Assoc/Disassoc/Reassoc AC + + IEEE 802.11 QoS + Classifying AC + Scheduling WTP/AC + Queuing WTP + + IEEE 802.11 RSN + IEEE 802.1X/EAP AC + RSNA Key Management AC + IEEE 802.11 Encryption/Decryption WTP/AC + + Figure 1: Mapping of 802.11 Functions for Split MAC Architecture + + In a Split MAC Architecture, the Distribution and Integration + services reside on the AC, and therefore all user data is tunneled + between the WTP and the AC. As noted above, all real-time IEEE + 802.11 services, including the Beacon and Probe Response frames, are + handled on the WTP. + + All remaining IEEE 802.11 MAC management frames are supported on the + AC, including the Association Request frame that allows the AC to be + involved in the access policy enforcement portion of the IEEE 802.11 + protocol. The IEEE 802.1X [IEEE.802-1X.2004], Extensible + Authentication Protocol (EAP) [RFC3748] and IEEE Robust Security + Network Association (RSNA) Key Management [IEEE.802-11.2007] + functions are also located on the AC. This implies that the + Authentication, Authorization, and Accounting (AAA) client also + resides on the AC. + + While the admission control component of IEEE 802.11 resides on the + AC, the real-time scheduling and queuing functions are on the WTP. + Note that this does not prevent the AC from providing additional + policy and scheduling functionality. + + Note that in the following figure, the use of '( - )' indicates that + processing of the frames is done on the WTP. This figure represents + a case where encryption services are provided by the AC. + + + + + + +Calhoun, et al. Standards Track [Page 8] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Client WTP AC + + Beacon + <----------------------------- + Probe Request + ----------------------------( - )-------------------------> + Probe Response + <----------------------------- + 802.11 AUTH/Association + <---------------------------------------------------------> + Station Configuration Request + [Add Station (Station MAC + Address), IEEE 802.11 Add + Station (WLAN ID), IEEE + 802.11 Session Key(Flag=A)] + <--------------------------> + 802.1X Authentication & 802.11 Key Exchange + <---------------------------------------------------------> + Station Configuration Request + [Add Station(Station MAC + Address), IEEE 802.11 Add + Station (WLAN ID), IEEE 802.11 + Station Session Key(Flag=C)] + <--------------------------> + 802.11 Action Frames + <---------------------------------------------------------> + 802.11 DATA (1) + <---------------------------( - )-------------------------> + + Figure 2: Split MAC Message Flow + + Figure 2 provides an illustration of the division of labor in a Split + MAC architecture. In this example, a WLAN has been created that is + configured for IEEE 802.11, using 802.1X-based end user + authentication and Advanced Encryption Standard-Counter Mode with + CBC-MAC Protocol (AES-CCMP) link layer encryption (CCMP, see + [FIPS.197.2001]). The following process occurs: + + o The WTP generates the IEEE 802.11 Beacon frames, using information + provided to it through the IEEE 802.11 Add WLAN (see Section 6.1) + message element, including the Robust Security Network Information + Element (RSNIE), which indicates support of 802.1X and AES-CCMP. + + o The WTP processes the Probe Request frame and responds with a + corresponding Probe Response frame. The Probe Request frame is + then forwarded to the AC for optional processing. + + + + + +Calhoun, et al. Standards Track [Page 9] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + o The WTP forwards the IEEEE 802.11 Authentication and Association + frames to the AC, which is responsible for responding to the + client. + + o Once the association is complete, the AC transmits a Station + Configuration Request message, which includes an Add Station + message element, to the WTP (see Section 4.6.8 in [RFC5415]). In + the above example, the WLAN was configured for IEEE 802.1X, and + therefore the IEEE 802.11 Station Session Key is included with the + flag field's 'A' bit set. + + o If the WTP is providing encryption/decryption services, once the + client has completed the IEEE 802.11 key exchange, the AC + transmits another Station Configuration Request message, which + includes: + + - An Add Station message element. + + - An IEEE 802.11 Add Station message element, which includes the + WLAN Identifier with which the station has associated. + + - An IEEE 802.11 Station Session Key message element, which + includes the pairwise encryption key. + + - An IEEE 802.11 Information Element message element, which + includes the Robust Security Network Information Element + (RSNIE) to the WTP, stating the security policy to enforce for + the client (in this case AES-CCMP). + + o If the WTP is providing encryption/decryption services, once the + client has completed the IEEE 802.11 key exchange, the AC + transmits another Station Configuration Request message, which + includes: + + - An Add Station message element. + + - An IEEE 802.11 Add Station message element, which includes the + WLAN Identifier with which the station has associated. + + - An IEEE 802.11 Station Session Key message element, which + includes the pairwise encryption key. + + - An IEEE 802.11 Information Element message element, which + includes the Robust Security Network Information Element + (RSNIE) to the WTP, stating the security policy to enforce for + the client (in this case AES-CCMP). + + + + + +Calhoun, et al. Standards Track [Page 10] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + o If the AC is providing encryption/decryption services, once the + client has completed the IEEE 802.11 key exchange, the AC + transmits another Station Configuration Request message, which + includes: + + - An Add Station message element. + + - An IEEE 802.11 Add Station message element, which includes the + WLAN Identifier with which the station has associated. + + - An IEEE 802.11 Station Session Key message element with the + flag field's 'C' bit enabled (indicating that the AC will + provide crypto services). + + o The WTP forwards any IEEE 802.11 Management Action frames received + to the AC. + + o All IEEE 802.11 station data frames are tunneled between the WTP + and the AC. + + Note that during the EAP over LAN (EAPOL)-Key exchange between the + Station and the AC, the Receive Sequence Counter (RSC) field for the + Group Key (GTK) needs to be included in the frame. The value of zero + (0) is used by the AC during this exchange. Additional details are + available in Section 9.1. + + The WTP SHALL include the IEEE 802.11 MAC header contents in all + frames transmitted to the AC. + + When 802.11 encryption/decryption is performed at the WTP, the WTP + MUST decrypt the uplink frames, MUST set the Protected Frame field to + 0, and MUST make the frame format consistent with that of an + unprotected 802.11 frame prior to transmitting the frames to the AC. + The fields added to an 802.11 protected frame (i.e., Initialization + Vector/Extended Initialization Vector (IV/EIV), Message Integrity + Code (MIC), and Integrity Check Value (ICV)) MUST be stripped off + prior to transmission from the WTP to AC. For downlink frames, the + Protected Frame field MUST be set to 0 by the AC as the frame being + sent is unencrypted. The WTP MUST apply the required protection + policy for the WLAN, and set the Protected Frame field on + transmission over the air. The Protected Frame field always needs to + accurately indicate the status of the 802.11 frame that is carrying + it. + + When 802.11 encryption/decryption is performed at the AC, the WTP + SHALL NOT decrypt the uplink frames prior to transmitting the frames + to the AC. The AC and WTP SHALL populate the IEEE 802.11 MAC header + fields as described in Figure 3. + + + +Calhoun, et al. Standards Track [Page 11] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + MAC header field Location + Frame Control: + Version AC + ToDS AC + FromDS AC + Type AC + SubType AC + MoreFrag WTP/AC + Retry WTP + Pwr Mgmt - + MoreData WTP + Protected WTP/AC + Order AC + Duration: WTP + Address 1: AC + Address 2: AC + Address 3: AC + Sequence Ctrl: WTP + Address 4: AC + QoS Control: AC + Frame Body: AC + FCS: WTP + + Figure 3: Population of the IEEE 802.11 MAC Header Fields for + Downlink Frames + + When 802.11 encryption/decryption is performed at the AC, the + MoreFrag bit is populated at the AC. The Pwr Mgmt bit is not + applicable to downlink frames, and is set to 0. Note that the Frame + Check Sequence (FCS) field is not included in 802.11 frames exchanged + between the WTP and the AC. Upon sending data frames to the AC, the + WTP is responsible for validating and stripping the FCS field. Upon + receiving data frames from the AC, the WTP is responsible for adding + the FCS field, and populating the field as described in + [IEEE.802-11.2007]. + + Note that when the WTP tunnels data packets to the AC (and vice + versa), the CAPWAP protocol does not guarantee in-order delivery. + When the protocol being transported over IEEE 802.11 is IP, out-of- + order delivery is not an issue as IP has no such requirements. + However, implementers need to be aware of this protocol + characteristic before deciding to use CAPWAP. + +2.2.2. Local MAC + + This section shows the division of labor between the WTP and the AC + in a Local MAC architecture. Figure 4 shows the separation of + functionality among CAPWAP components. + + + +Calhoun, et al. Standards Track [Page 12] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Function Location + Distribution Service WTP/AC + Integration Service WTP + Beacon Generation WTP + Probe Response Generation WTP + Power Mgmt/Packet Buffering WTP + Fragmentation/Defragmentation WTP + Assoc/Disassoc/Reassoc WTP/AC + + IEEE 802.11 QoS + Classifying WTP + Scheduling WTP + Queuing WTP + + IEEE 802.11 RSN + IEEE 802.1X/EAP AC + RSNA Key Management AC + IEEE 802.11 Encryption/Decryption WTP + + Figure 4: Mapping of 802.11 Functions for Local AP Architecture + + In the Local MAC mode, the integration service exists on the WTP, + while the distribution service MAY reside on either the WTP or the + AC. When it resides on the AC, station-generated frames are not + forwarded to the AC in their native format, but encapsulated as 802.3 + frames. + + While the MAC is terminated on the WTP, it is necessary for the AC to + be aware of mobility events within the WTPs. Thus, the WTP MUST + forward the IEEE 802.11 Association Request frames to the AC. The AC + MAY reply with a failed Association Response frame if it deems it + necessary, and upon receipt of a failed Association Response frame + from the AC, the WTP MUST send a Disassociation frame to the station. + + The IEEE 802.1X [IEEE.802-1X.2004], EAP, and IEEE RSNA Key Management + [IEEE.802-11.2007] functions reside in the AC. Therefore, the WTP + MUST forward all IEEE 802.1X, EAP, and RSNA Key Management frames to + the AC and forward the corresponding responses to the station. This + implies that the AAA client also resides on the AC. + + Note that in the following figure, the use of '( - )' indicates that + processing of the frames is done on the WTP. + + + + + + + + + +Calhoun, et al. Standards Track [Page 13] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Client WTP AC + + Beacon + <----------------------------- + Probe + <----------------------------> + 802.11 AUTH + <----------------------------- + 802.11 Association + <---------------------------( - )-------------------------> + Station Configuration Request + [Add Station (Station MAC + Address), IEEE 802.11 Add + Station (WLAN ID), IEEE + 802.11 Session Key(Flag=A)] + <--------------------------> + 802.1X Authentication & 802.11 Key Exchange + <---------------------------------------------------------> + Station Configuration Request + [Add Station(Station MAC + Address), IEEE 802.11 Add + Station (WLAN ID), IEEE 802.11 + Station session Key (Key=x), + IEEE 802.11 Information + Element(RSNIE(Pairwise + Cipher=CCMP))] + <--------------------------> + 802.11 Action Frames + <---------------------------------------------------------> + 802.11 DATA + <-----------------------------> + + Figure 5: Local MAC Message Flow + + Figure 5 provides an illustration of the division of labor in a Local + MAC architecture. In this example, a WLAN that is configured for + IEEE 802.11 has been created using AES-CCMP for privacy. The + following process occurs: + + o The WTP generates the IEEE 802.11 Beacon frames, using information + provided to it through the Add WLAN (see Section 6.1) message + element. + + o The WTP processes a Probe Request frame and responds with a + corresponding Probe Response frame. + + o The WTP forwards the IEEE 802.11 Authentication and Association + frames to the AC. + + + +Calhoun, et al. Standards Track [Page 14] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + o Once the association is complete, the AC transmits a Station + Configuration Request message, which includes the Add Station + message element, to the WTP (see Section 4.6.8 in [RFC5415]). In + the above example, the WLAN was configured for IEEE 802.1X, and + therefore the IEEE 802.11 Station Session Key is included with the + flag field's 'A' bit set. + + o The WTP forwards all IEEE 802.1X and IEEE 802.11 key exchange + messages to the AC for processing. + + o The AC transmits another Station Configuration Request message, + which includes: + + - An Add Station message element, which MAY include a Virtual LAN + (VLAN) [IEEE.802-1Q.2005] name, which when present is used by + the WTP to identify the VLAN on which the user's data frames + are to be bridged. + + - An IEEE 802.11 Add Station message element, which includes the + WLAN Identifier with which the station has associated. + + - An IEEE 802.11 Station Session Key message element, which + includes the pairwise encryption key. + + - An IEEE 802.11 Information Element message element, which + includes the RSNIE to the WTP, stating the security policy to + enforce for the client (in this case AES-CCMP). + + o The WTP forwards any IEEE 802.11 Management Action frames received + to the AC. + + o The WTP MAY locally bridge client data frames (and provide the + necessary encryption and decryption services). The WTP MAY also + tunnel client data frames to the AC, using 802.3 frame tunnel mode + or 802.11 frame tunnel mode. + +2.3. Roaming Behavior + + This section expands upon the examples provided in the previous + section, and describes how the CAPWAP control protocol is used to + provide secure roaming. + + Once a client has successfully associated with the network in a + secure fashion, it is likely to attempt to roam to another WTP. + Figure 6 shows an example of a currently associated station moving + from its "Old WTP" to a "New WTP". The figure is valid for multiple + different security policies, including IEEE 802.1X and Wireless + Protected Access (WPA) or Wireless Protected Access 2 (WPA2) [WPA]. + + + +Calhoun, et al. Standards Track [Page 15] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + In the event that key caching was employed, the 802.1X Authentication + step would be eliminated. Note that the example represents one where + crypto services are provided by the WTP, so in a case where the AC + provided this function the last Station Configuration Request would + be different. + + Client Old WTP New WTP AC + + Association Request/Response + <--------------------------------------( - )--------------> + Station Configuration Request + [Add Station (Station MAC + Address), IEEE 802.11 Add + Station (WLAN ID), IEEE + 802.11 Session Key(Flag=A)] + <----------------> + 802.1X Authentication (if no key cache entry exists) + <--------------------------------------( - )--------------> + 802.11 4-way Key Exchange + <--------------------------------------( - )--------------> + Station Configuration Request + [Delete Station] + <----------------------------------> + Station Configuration Request + [Add Station(Station MAC + Address), IEEE 802.11 Add + Station (WLAN ID), IEEE 802.11 + Station session Key (Key=x), + IEEE 802.11 Information + Element(RSNIE(Pairwise + Cipher=CCMP))] + <----------------> + + Figure 6: Client Roaming Example + +2.4. Group Key Refresh + + Periodically, the Group Key (GTK) for the BSS needs to be updated. + The AC uses an EAPOL-Key frame to update the group key for each STA + in the BSS. While the AC is updating the GTK, each Layer 2 (L2) + broadcast frame transmitted to the BSS needs to be duplicated and + transmitted using both the current GTK and the new GTK. Once the GTK + update process has completed, broadcast frames transmitted to the BSS + will be encrypted using the new GTK. + + In the case of Split MAC, the AC needs to duplicate all broadcast + packets and update the key index so that the packet is transmitted + using both the current and new GTK to ensure that all STAs in the BSS + + + +Calhoun, et al. Standards Track [Page 16] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + receive the broadcast frames. In the case of Local MAC, the WTP + needs to duplicate and transmit broadcast frames using the + appropriate index to ensure that all STAs in the BSS continue to + receive broadcast frames. + + The Group Key update procedure is shown in the following figure. The + AC will signal the update to the GTK using an IEEE 802.11 + Configuration Request message, including an IEEE 802.11 Update WLAN + message element with the new GTK, its index, the Transmit Sequence + Counter (TSC) for the Group Key and the Key Status set to 3 (begin + GTK update). The AC will then begin updating the GTK for each STA. + During this time, the AC (for Split MAC) or WTP (for Local MAC) MUST + duplicate broadcast packets and transmit them encrypted with both the + current and new GTK. When the AC has completed the GTK update to all + STAs in the BSS, the AC MUST transmit an IEEE 802.11 Configuration + Request message including an IEEE 802.11 Update WLAN message element + containing the new GTK, its index, and the Key Status set to 4 (GTK + update complete). + + Client WTP AC + + IEEE 802.11 WLAN Configuration Request [Update + WLAN (GTK, GTK Index, GTK Start, + Group TSC) ] + <-------------------------------------------- + 802.1X EAPoL (GTK Message 1) + <-------------( - )------------------------------------------- + 802.1X EAPoL (GTK Message 2) + -------------( - )-------------------------------------------> + IEEE 802.11 WLAN Configuration Request [ Update + WLAN (GTK Index, GTK Complete) ] + <-------------------------------------------- + + Figure 7: Group Key Update Procedure + +2.5. BSSID to WLAN ID Mapping + + The CAPWAP protocol binding enables the WTP to assign BSSIDs upon + creation of a WLAN (see Section 6.1). While manufacturers are free + to assign BSSIDs using any arbitrary mechanism, it is advised that + where possible the BSSIDs are assigned as a contiguous block. + + When assigned as a block, implementations can still assign any of the + available BSSIDs to any WLAN. One possible method is for the WTP to + assign the address using the following algorithm: base BSSID address + + WLAN ID. + + + + + +Calhoun, et al. Standards Track [Page 17] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + The WTP communicates the maximum number of BSSIDs that it supports + during configuration via the IEEE 802.11 WTP WLAN Radio Configuration + message element (see Section 6.23). + +2.6. CAPWAP Data Channel QoS Behavior + + The CAPWAP IEEE 802.11 binding specification provides procedures to + allow for the WTP to enforce Quality of Service on IEEE 802.11 Data + Frames and MAC Management messages. + +2.6.1. IEEE 802.11 Data Frames + + When the WLAN is created on the WTP, a default Quality of Service + policy is established through the IEEE 802.11 WTP Quality of Service + message element (see Section 6.22). This default policy will cause + the WTP to use the default QoS values for any station associated with + the WLAN in question. The AC MAY also override the policy for a + given station by sending the IEEE 802.11 Update Station QoS message + element (see Section 6.20), known as a station-specific QoS policy. + + Beyond the default, and per station QoS policy, the IEEE 802.11 + protocol also allows a station to request special QoS treatment for a + specific flow through the Traffic Specification (TSPEC) Information + Elements found in the IEEE 802.11-2007's QoS Action Frame. + Alternatively, stations MAY also use the WiFi Alliance's WMM + specification instead to request QoS treatment for a flow (see + [WMM]). This requires the WTP to observe the Status Code in the IEEE + 802.11-2007 and WMM QoS Action Add Traffic System (ADDTS) responses + from the AC, and provide the services requested in the TSPEC + Information Element. Similarly, the WTP MUST observe the Reason Code + Information Element in the IEEE 802.11-2007 and WMM QoS Action DELTS + responses from the AC by removing the policy associated with the + TSPEC. + + The IEEE 802.11 WTP Quality of Service message element's Tagging + Policy field indicates how the packets are to be tagged, known as the + Tagging Policy. There are five bits defined, two of which are used + to indicate the type of QoS to be used by the WTP. The first is the + 'P' bit, which is set to inform the WTP it is to use the 802.1p QoS + mechanism. When set, the 'Q' bit is used to inform the WTP which + 802.1p priority values it is to use. + + The 'D' bit is set to inform the WTP it is to use the Differentiated + Services Code Point (DSCP) QoS mechanism. When set, the 'I' and 'O' + bits are used to inform the WTP which values it is to use in the + inner header, in the station's original packet, or the outer header, + the latter of which is only valid when tunneling is enabled. + + + + +Calhoun, et al. Standards Track [Page 18] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + When an IEEE 802.11 Update Station QoS message element is received, + while the specific 802.1p priority or DSCP values may change for a + given station, known as the station specific policy, the original + Tagging Policy (the use of the five bits) remains the same. + + The use of the DSCP and 802.1p QoS mechanisms are not mutually + exclusive. An AC MAY request that a WTP use none, one, or both types + of QoS mechanisms at the same time. + +2.6.1.1. 802.1p Support + + The IEEE 802.11 WTP Quality of Service and IEEE 802.11 Update Station + QoS message elements include the "802.1p Tag" field, which is the + 802.1p priority value. This value is used by the WTP by adding an + 802.1Q header (see [IEEE.802-1Q.2005]) with the priority field set + according to the policy provided. Note that this tagging is only + valid for interfaces that support 802.1p. The actual treatment does + not change for either Split or Local MAC modes, or when tunneling is + used. The only exception is when tunneling is used, the 802.1Q + header is added to the outer packet (tunneled) header. The IEEE + 802.11 standard does not permit the station's packet to include an + 802.1Q header. Instead, the QoS mechanisms defined in the IEEE + 802.11 standard are used by stations to mark a packet's priority. + When the 'P' bit is set in the Tagging Policy, the 'Q' bit has the + following behavior: + + Q=1: The WTP marks the priority field in the 802.1Q header to + either the default or the station-specific 802.1p policy. + + Q=0: The WTP marks the priority field in the 802.1Q header to the + value found in the User Priority field of the QoS Control + field of the IEEE 802.11 header. If the QoS Control field is + not present in the IEEE 802.11 header, then the behavior + described under 'Q=1' is used. + +2.6.1.2. DSCP Support + + The IEEE 802.11 WTP Quality of Service and IEEE 802.11 Update Station + QoS message elements also provide a "DSCP Tag", which is used by the + WTP when the 'D' bit is set to mark the DSCP field of both the IPv4 + and IPv6 headers (see [RFC2474]). When DSCP is used, the WTP marks + the inner packet (the original packet received by the station) when + the 'I' bit is set. Similarly, the WTP marks the outer packet + (tunnel header's DSCP field) when the 'O' bit is set. + + When the 'D' bit is set, the treatment of the packet differs based on + whether the WTP is tunneling the station's packets to the AC. + Tunneling does not occur in a Local MAC mode when the AC has + + + +Calhoun, et al. Standards Track [Page 19] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + communicated that tunneling is not required, as part of the IEEE + 802.11 Add WLAN message element, see Section 6.1. In the case where + tunneling is not used, the 'I' and 'O' bits have the following + behaviors: + + O=1: This option is invalid when tunneling is not enabled for + station data frames. + + O=0: This option is invalid when tunneling is not enabled for + station data frames. + + I=1: The WTP sets the DSCP field in the station's packet to either + the default policy or the station-specific policy if one + exists. + + I=0: The WTP MUST NOT modify the DSCP field in the station's + packet. + + For Split MAC mode, or Local MAC with tunneling enabled, the WTP + needs to contend with both the inner packet (the station's original + packet) as well as the tunnel header (added by the WTP). In this + mode of operation, the bits are treated as follows: + + O=1: The WTP sets the DSCP field in the tunnel header to either the + default policy or the station specific policy if one exists. + + O=0: The WTP sets the DSCP field in the tunnel header to the value + found in the inner packet's DSCP field. If encryption + services are provided by the AC (see Section 6.15), the packet + is encrypted; therefore, the WTP cannot access the inner DSCP + field, in which case it uses the behavior described when the + 'O' bit is set. This occurs also if the inner packet is not + IPv4 or IPv6, and thus does not have a DSCP field. + + I=1: The WTP sets the DSCP field in the station's packet to either + the default policy or the station-specific policy if one + exists. If encryption services are provided by the AC (see + Section 6.15), the packet is encrypted; therefore, the WTP + cannot access the inner DSCP field, in which case it uses the + behavior described when the 'I' bit is not set. This occurs + also if the inner packet is not IPv4 or IPv6, and thus does + not have a DSCP field. + + I=0: The WTP MUST NOT modify the DSCP field in the station's + packet. + + + + + + +Calhoun, et al. Standards Track [Page 20] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + The CAPWAP protocol supports the Explicit Congestion Notification + (ECN) bits [RFC3168]. Additional details on ECN support can be found + in [RFC5415]. + +2.6.2. IEEE 802.11 MAC Management Messages + + It is recommended that IEEE 802.11 MAC Management frames be sent by + both the AC and the WTP with appropriate Quality of Service values, + listed below, to ensure that congestion in the network minimizes + occurrences of packet loss. Note that the QoS Mechanism specified in + the Tagging Policy is used as specified by the AC in the IEEE 802.11 + WTP Quality of Service message element (see Section 6.22). However, + the station-specific policy is not used for IEEE 802.11 MAC + Management frames. + + 802.1p: The precedence value of 7 (decimal) SHOULD be used for all + IEEE 802.11 MAC management frames, except for Probe + Requests, which SHOULD use 4. + + DSCP: All IEEE 802.11 MAC management frames SHOULD use the CS6 + per- hop behavior (see [RFC2474]), while IEEE 802.11 Probe + Requests should use the Low Drop Assured Forwarding per-hop + behavior (see [RFC3246]). + +2.7. Run State Operation + + The Run state is the normal state of operation for the CAPWAP + protocol in both the WTP and the AC. + + When the WTP receives a WLAN Configuration Request message (see + Section 3.1), it MUST respond with a WLAN Configuration Response + message (see Section 3.2), and it remains in the Run state. + + When the AC sends a WLAN Configuration Request message (see + Section 3.1) or receives the corresponding WLAN Configuration + Response message (see Section 3.2) from the WTP, it remains in the + Run state. + +3. IEEE 802.11 Specific CAPWAP Control Messages + + This section defines CAPWAP Control messages that are specific to the + IEEE 802.11 binding. Two messages are defined: IEEE 802.11 WLAN + Configuration Request and IEEE 802.11 WLAN Configuration Response. + See Section 4.5 in [RFC5415] for CAPWAP Control message definitions + and the derivation of the Message Type value from the IANA Enterprise + number. + + + + + +Calhoun, et al. Standards Track [Page 21] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + The valid message types for IEEE 802.11-specific control messages are + listed below. The IANA Enterprise number used with these messages is + 13277. + + CAPWAP Control Message Message Type + Value + + IEEE 802.11 WLAN Configuration Request 3398913 + IEEE 802.11 WLAN Configuration Response 3398914 + +3.1. IEEE 802.11 WLAN Configuration Request + + The IEEE 802.11 WLAN Configuration Request is sent by the AC to the + WTP in order to change services provided by the WTP. This control + message is used to either create, update, or delete a WLAN on the + WTP. + + The IEEE 802.11 WLAN Configuration Request is sent as a result of + either some manual administrative process (e.g., deleting a WLAN), or + automatically to create a WLAN on a WTP. When sent automatically to + create a WLAN, this control message is sent after the CAPWAP + Configuration Update Response message (see Section 8.5 in [RFC5415]) + has been received by the AC. + + Upon receiving this control message, the WTP will modify the + necessary services and transmit an IEEE 802.11 WLAN Configuration + Response. + + A WTP MAY provide service for more than one WLAN; therefore, every + WLAN is identified through a numerical index. For instance, a WTP + that is capable of supporting up to 16 Service Set Identifiers + (SSIDs), could accept up to 16 IEEE 802.11 WLAN Configuration Request + messages that include the Add WLAN message element. + + Since the index is the primary identifier for a WLAN, an AC MAY + attempt to ensure that the same WLAN is identified through the same + index number on all of its WTPs. An AC that does not follow this + approach MUST find some other means of maintaining a WLAN-Identifier- + to-SSID mapping table. + + The following message elements MAY be included in the IEEE 802.11 + WLAN Configuration Request message. Only one message element MUST be + present. + + o IEEE 802.11 Add WLAN, see Section 6.1 + + o IEEE 802.11 Delete WLAN, see Section 6.4 + + + + +Calhoun, et al. Standards Track [Page 22] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + o IEEE 802.11 Update WLAN, see Section 6.21 + + The following message element MAY be present. + + o IEEE 802.11 Information Element, see Section 6.6 + + o Vendor-Specific Payload, see [RFC5415] + +3.2. IEEE 802.11 WLAN Configuration Response + + The IEEE 802.11 WLAN Configuration Response message is sent by the + WTP to the AC. It is used to acknowledge receipt of an IEEE 802.11 + WLAN Configuration Request message, and to indicate that the + requested configuration was successfully applied or that an error + related to the processing of the IEEE 802.11 WLAN Configuration + Request message occurred on the WTP. + + The following message element MUST be included in the IEEE 802.11 + WLAN Configuration Response message. + + o Result Code, see Section 4.6.34 in [RFC5415] + + The following message element MAY be included in the IEEE 802.11 WLAN + Configuration Response message. + + o IEEE 802.11 Assigned WTP BSSID, see Section 6.3 + + o Vendor-Specific Payload, see [RFC5415] + +4. CAPWAP Data Message Bindings + + This section describes the CAPWAP data message bindings to support + transport of IEEE 802.11 frames. + + Payload encapsulation: The CAPWAP protocol defines the CAPWAP data + message, which is used to encapsulate a wireless payload. For + IEEE 802.11, the IEEE 802.11 header and payload are encapsulated + (excluding the IEEE 802.11 FCS checksum). The IEEE 802.11 FCS + checksum is handled by the WTP. This allows the WTP to validate + an IEEE 802.11 frame prior to sending it to the AC. Similarly, + when an AC wishes to transmit a frame to a station, the WTP + computes and adds the FCS checksum. + + Optional Wireless Specific Information: This optional CAPWAP header + field (see Section 4.3 in [RFC5415]) is only used with CAPWAP data + messages, and it serves two purposes, depending upon the direction + of the message. For messages from the WTP to the AC, the field + uses the format described in the "IEEE 802.11 Frame Info" field + + + +Calhoun, et al. Standards Track [Page 23] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + (see below). However, for messages sent by the AC to the WTP, the + format used is described in the "Destination WLANs" field (also + defined below). + + Note that in both cases, the two optional headers fit in the + "Data" field of the Wireless Specific Information header. + + IEEE 802.11 Frame Info: When an IEEE 802.11 frame is received from a + station over the air, it is encapsulated and this field is used to + include radio and PHY-specific information associated with the + frame. + + The IEEE 802.11 Frame Info field has the following format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | RSSI | SNR | Data Rate | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + RSSI: Received Signal Strength Indication (RSSI) is a signed, + 8-bit value. It is the received signal strength indication, in + dBm. + + SNR: SNR is a signed, 8-bit value. It is the signal-to-noise + ratio of the received IEEE 802.11 frame, in dB. + + Data Rate: The data rate field is a 16-bit unsigned value. The + data rate field is a 16-bit unsigned value expressing the data + rate of the packets received by the WTP in units of 0.1 Mbps. + For instance, a packet received at 5.5 Mbps would be set to 55, + while 11 Mbps would be set to 110. + + Destination WLANs: The Destination WLANs field is used to specify + the target WLANs for a given frame, and is only used with + broadcast and multicast frames. This field allows the AC to + transmit a single broadcast or multicast frame to the WTP and + allows the WTP to perform the necessary frame replication. The + field uses the following format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | WLAN ID bitmap | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + + + +Calhoun, et al. Standards Track [Page 24] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + WLAN ID bitmap: This bit field indicates the WLAN ID (see + Section 6.1) on which the WTP will transmit the included frame. + For instance, if a multicast packet is to be transmitted on + WLANs 1 and 3, the bits for WLAN 1 and 3 of this field would be + enabled. WLAN 1 is represented by bit 15 in the figure above, + or the least significant bit, while WLAN 16 would be + represented by bit zero (0), or the most significant bit, in + the figure. This field is to be set to all zeroes for unicast + packets and is unused if the WTP is not providing IEEE 802.11 + encryption. + + Reserved: All implementations complying with this protocol MUST + set to zero any bits that are reserved in the version of the + protocol supported by that implementation. Receivers MUST + ignore all bits not defined for the version of the protocol + they support. + +5. CAPWAP Control Message Bindings + + This section describes the IEEE 802.11-specific message elements + included in CAPWAP Control Messages. + +5.1. Discovery Request Message + + The following IEEE 802.11-specific message element MUST be included + in the CAPWAP Discovery Request Message. + + o IEEE 802.11 WTP Radio Information, see Section 6.25. An IEEE + 802.11 WTP Radio Information message element MUST be present for + every radio in the WTP. + +5.2. Discovery Response Message + + The following IEEE 802.11-specific message element MUST be included + in the CAPWAP Discovery Response Message. + + o IEEE 802.11 WTP Radio Information, see Section 6.25. An IEEE + 802.11 WTP Radio Information message element MUST be present for + every radio in the WTP. + +5.3. Primary Discovery Request Message + + The following IEEE 802.11 specific message element MUST be included + in the CAPWAP Primary Discovery Request message. + + o IEEE 802.11 WTP Radio Information, see Section 6.25. An IEEE + 802.11 WTP Radio Information message element MUST be present for + every radio in the WTP. + + + +Calhoun, et al. Standards Track [Page 25] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + +5.4. Primary Discovery Response Message + + The following IEEE 802.11-specific message element MUST be included + in the CAPWAP Primary Discovery Response message. + + o IEEE 802.11 WTP Radio Information, see Section 6.25. An IEEE + 802.11 WTP Radio Information message element MUST be present for + every radio in the WTP. + +5.5. Join Request Message + + The following IEEE 802.11-specific message element MUST be included + in the CAPWAP Join Request message. + + o IEEE 802.11 WTP Radio Information, see Section 6.25. An IEEE + 802.11 WTP Radio Information message element MUST be present for + every radio in the WTP. + +5.6. Join Response Message + + The following IEEE 802.11-specific message element MUST be included + in the CAPWAP Join Response message. + + o IEEE 802.11 WTP Radio Information, see Section 6.25. An IEEE + 802.11 WTP Radio Information message element MUST be present for + every radio in the WTP. + +5.7. Configuration Status Request Message + + The following IEEE 802.11-specific message elements MAY be included + in the CAPWAP Configuration Status Request message. More than one of + each message element listed MAY be included. + + o IEEE 802.11 Antenna, see Section 6.2 + + o IEEE 802.11 Direct Sequence Control, see Section 6.5 + + o IEEE 802.11 MAC Operation, see Section 6.7 + + o IEEE 802.11 Multi-Domain Capability, see Section 6.9 + + o IEEE 802.11 Orthogonal Frequency Division Multiplexing (OFDM) + Control, see Section 6.10 + + o IEEE 802.11 Supported Rates, see Section 6.17 + + o IEEE 802.11 Tx Power, see Section 6.18 + + + + +Calhoun, et al. Standards Track [Page 26] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + o IEEE 802.11 TX Power Level, see Section 6.19 + + o IEEE 802.11 WTP Radio Configuration, see Section 6.23 + + o IEEE 802.11 WTP Radio Information, see Section 6.25. An IEEE + 802.11 WTP Radio Information message element MUST be present for + every radio in the WTP. + +5.8. Configuration Status Response Message + + The following IEEE 802.11 specific message elements MAY be included + in the CAPWAP Configuration Status Response Message. More than one + of each message element listed MAY be included. + + o IEEE 802.11 Antenna, see Section 6.2 + + o IEEE 802.11 Direct Sequence Control, see Section 6.5 + + o IEEE 802.11 MAC Operation, see Section 6.7 + + o IEEE 802.11 Multi-Domain Capability, see Section 6.9 + + o IEEE 802.11 OFDM Control, see Section 6.10 + + o IEEE 802.11 Rate Set, see Section 6.11 + + o IEEE 802.11 Supported Rates, see Section 6.17 + + o IEEE 802.11 Tx Power, see Section 6.18 + + o IEEE 802.11 WTP Quality of Service, see Section 6.22 + + o IEEE 802.11 WTP Radio Configuration, see Section 6.23 + +5.9. Configuration Update Request Message + + The following IEEE 802.11-specific message elements MAY be included + in the CAPWAP Configuration Update Request message. More than one of + each message element listed MAY be included. + + o IEEE 802.11 Antenna, see Section 6.2 + + o IEEE 802.11 Direct Sequence Control, see Section 6.5 + + o IEEE 802.11 MAC Operation, see Section 6.7 + + o IEEE 802.11 Multi-Domain Capability, see Section 6.9 + + + + +Calhoun, et al. Standards Track [Page 27] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + o IEEE 802.11 OFDM Control, see Section 6.10 + + o IEEE 802.11 Rate Set, see Section 6.11 + + o IEEE 802.11 RSNA Error Report from Station, see Section 6.12 + + o IEEE 802.11 Tx Power, see Section 6.18 + + o IEEE 802.11 WTP Quality of Service, see Section 6.22 + + o IEEE 802.11 WTP Radio Configuration, see Section 6.23 + +5.10. Station Configuration Request + + The following IEEE 802.11-specific message elements MAY be included + in the CAPWAP Station Configuration Request message. More than one + of each message element listed MAY be included. + + o IEEE 802.11 Station, see Section 6.13 + + o IEEE 802.11 Station Session Key, see Section 6.15 + + o IEEE 802.11 Station QoS Profile, see Section 6.14 + + o IEEE 802.11 Update Station Qos, see Section 6.20 + +5.11. Change State Event Request + + The following IEEE 802.11-specific message element MAY be included in + the CAPWAP Station Configuration Request message. + + o IEEE 802.11 WTP Radio Fail Alarm Indication, see Section 6.24 + +5.12. WTP Event Request + + The following IEEE 802.11-specific message elements MAY be included + in the CAPWAP WTP Event Request message. More than one of each + message element listed MAY be included. + + o IEEE 802.11 MIC Countermeasures, see Section 6.8 + + o IEEE 802.11 RSNA Error Report from Station, see Section 6.12 + + o IEEE 802.11 Statistics, see Section 6.16 + + + + + + + +Calhoun, et al. Standards Track [Page 28] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + +6. IEEE 802.11 Message Element Definitions + + The following IEEE 802.11-specific message elements are defined in + this section. + + IEEE 802.11 Message Element Type Value + + IEEE 802.11 Add WLAN 1024 + IEEE 802.11 Antenna 1025 + IEEE 802.11 Assigned WTP BSSID 1026 + IEEE 802.11 Delete WLAN 1027 + IEEE 802.11 Direct Sequence Control 1028 + IEEE 802.11 Information Element 1029 + IEEE 802.11 MAC Operation 1030 + IEEE 802.11 MIC Countermeasures 1031 + IEEE 802.11 Multi-Domain Capability 1032 + IEEE 802.11 OFDM Control 1033 + IEEE 802.11 Rate Set 1034 + IEEE 802.11 RSNA Error Report From Station 1035 + IEEE 802.11 Station 1036 + IEEE 802.11 Station QoS Profile 1037 + IEEE 802.11 Station Session Key 1038 + IEEE 802.11 Statistics 1039 + IEEE 802.11 Supported Rates 1040 + IEEE 802.11 Tx Power 1041 + IEEE 802.11 Tx Power Level 1042 + IEEE 802.11 Update Station QoS 1043 + IEEE 802.11 Update WLAN 1044 + IEEE 802.11 WTP Quality of Service 1045 + IEEE 802.11 WTP Radio Configuration 1046 + IEEE 802.11 WTP Radio Fail Alarm Indication 1047 + IEEE 802.11 WTP Radio Information 1048 + + Figure 8: IEEE 802.11 Binding Message Elements + +6.1. IEEE 802.11 Add WLAN + + The IEEE 802.11 Add WLAN message element is used by the AC to define + a WLAN on the WTP. The inclusion of this message element MUST also + include IEEE 802.11 Information Element message elements, containing + the following IEEE 802.11 IEs: + + Power Constraint information element + + EDCA Parameter Set information element + + QoS Capability information element + + + + +Calhoun, et al. Standards Track [Page 29] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + WPA information element [WPA] + + RSN information element + + WMM information element [WMM] + + These IEEE 802.11 Information Elements are stored by the WTP and + included in any Probe Responses and Beacons generated, as specified + in the IEEE 802.11 standard [IEEE.802-11.2007]. If present, the RSN + Information Element is sent with the IEEE 802.11 Add WLAN message + element to instruct the WTP on the usage of the Key field. + + If cryptographic services are provided at the WTP, the WTP MUST + observe the algorithm dictated in the Group Cipher Suite field of the + RSN Information Element sent by the AC. The RSN Information Element + is used to communicate any supported algorithm, including WEP, + Temporal Key Integrity Protocol (TKIP) and AES-CCMP. In the case of + static WEP keys, the RSN Information Element is still used to + indicate the cryptographic algorithm even though no key exchange + occurred. + + An AC MAY include additional Information Elements as desired. The + message element uses the following format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | WLAN ID | Capability | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Key Index | Key Status | Key Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Key... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Group TSC | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Group TSC | QoS | Auth Type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MAC Mode | Tunnel Mode | Suppress SSID | SSID ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1024 for IEEE 802.11 Add WLAN + + Length: >= 20 + + Radio ID: An 8-bit value representing the radio, whose value is + between one (1) and 31. + + + + + +Calhoun, et al. Standards Track [Page 30] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + WLAN ID: An 8-bit value specifying the WLAN Identifier. The value + MUST be between one (1) and 16. + + Capability: A 16-bit value containing the Capability information + field to be advertised by the WTP in the Probe Request and Beacon + frames. Each bit of the Capability field represents a different + WTP capability, which are described in detail in + [IEEE.802-11.2007]. The format of the field is: + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E|I|C|F|P|S|B|A|M|Q|T|D|V|O|K|L| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + E (ESS): The AC MUST set the Extended Service Set (ESS) subfield + to 1. + + I (IBSS): The AC MUST set the Independent Basic Service Set + (IBSS) subfield to 0. + + C (CF-Pollable): The AC sets the Contention Free Pollable (CF- + Pollable) subfield based on the table found in + [IEEE.802-11.2007]. + + F (CF-Poll Request): The AC sets the CF-Poll Request subfield + based on the table found in [IEEE.802-11.2007]. + + P (Privacy): The AC sets the Privacy subfield based on the + confidentiality requirements of the WLAN, as defined in + [IEEE.802-11.2007]. + + S (Short Preamble): The AC sets the Short Preamble subfield + based on whether the use of short preambles is permitted on the + WLAN, as defined in [IEEE.802-11.2007]. + + B (PBCC): The AC sets the Packet Binary Convolutional Code + (PBCC) modulation option subfield based on whether the use of + PBCC is permitted on the WLAN, as defined in [IEEE.802-11.2007]. + + A (Channel Agility): The AC sets the Channel Agility subfield + based on whether the WTP is capable of supporting the High Rate + Direct Sequence Spread Spectrum (HR/DSSS), as defined in + [IEEE.802-11.2007]. + + + + + + + +Calhoun, et al. Standards Track [Page 31] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + M (Spectrum Management): The AC sets the Spectrum Management + subfield according to the value of the + dot11SpectrumManagementRequired MIB variable, as defined in + [IEEE.802-11.2007]. + + Q (QoS): The AC sets the Quality of Service (QoS) subfield based + on the table found in [IEEE.802-11.2007]. + + T (Short Slot Time): The AC sets the Short Slot Time subfield + according to the value of the WTP's currently used slot time + value, as defined in [IEEE.802-11.2007]. + + D (APSD): The AC sets the Automatic Power Save Delivery (APSD) + subfield according to the value of the + dot11APSDOptionImplemented Management Information Base (MIB) + variable, as defined in [IEEE.802-11.2007]. + + V (Reserved): The AC sets the Reserved subfield to zero, as + defined in [IEEE.802-11.2007]. + + O (DSSS-OFDM): The AC sets the DSSS-OFDM subfield to indicate + the use of Direct Sequence Spread Spectrum with Orthogonal + Frequency Division Multiplexing (DSSS-OFDM), as defined in + [IEEE.802-11.2007]. + + K (Delayed Block ACK): The AC sets the Delayed Block ACK + subfield according to the value of the + dot11DelayedBlockAckOptionImplemented MIB variable, as defined + in [IEEE.802-11.2007]. + + L (Immediate Block ACK): The AC sets the Delayed Block ACK + subfield according to the value of the + dot11ImmediateBlockAckOptionImplemented MIB variable, as defined + in [IEEE.802-11.2007]. + + Key-Index: The Key Index associated with the key. + + Key Status: A 1-byte value that specifies the state and usage of + the key that has been included. Note this field is ignored if the + Key Length field is set to zero (0). The following values + describe the key usage and its status: + + 0 - A value of zero, with the inclusion of the RSN Information + Element means that the WLAN uses per-station encryption keys, + and therefore the key in the 'Key' field is only used for + multicast traffic. + + + + + +Calhoun, et al. Standards Track [Page 32] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + 1 - When set to one, the WLAN employs a shared Wired Equivalent + Privacy (WEP) key, also known as a static WEP key, and uses + the encryption key for both unicast and multicast traffic for + all stations. + + 2 - The value of 2 indicates that the AC will begin rekeying the + GTK with the STA's in the BSS. It is only valid when IEEE + 802.11 is enabled as the security policy for the BSS. + + 3 - The value of 3 indicates that the AC has completed rekeying + the GTK and broadcast packets no longer need to be duplicated + and transmitted with both GTK's. + + Key Length: A 16-bit value representing the length of the Key + field. + + Key: A Session Key, whose length is known via the Key Length field, + used to provide data privacy. For encryption schemes that employ + a separate encryption key for unicast and multicast traffic, the + key included here only applies to multicast frames, and the cipher + suite is specified in an accompanied RSN Information Element. In + these scenarios, the key and cipher information is communicated + via the Add Station message element, see Section 4.6.8 in + [RFC5415] and the IEEE 802.11 Station Session Key message element, + see Section 6.15. When used with WEP, the key field includes the + broadcast key. When used with CCMP, the Key field includes the + 128-bit Group Temporal Key. When used with TKIP, the Key field + includes the 256-bit Group Temporal Key (which consists of a 128- + bit key used as input for TKIP key mixing, and two 64-bit keys + used for Michael). + + Group TSC: A 48-bit value containing the Transmit Sequence Counter + (TSC) for the updated group key. The WTP will set the TSC for + broadcast/multicast frames to this value for the updated group + key. + + QoS: An 8-bit value specifying the default QoS policy for the WTP + to apply to network traffic received for a non-WMM enabled STA. + + The following enumerated values are supported: + + 0 - Best Effort + + 1 - Video + + + + + + + +Calhoun, et al. Standards Track [Page 33] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + 2 - Voice + + 3 - Background + + Auth Type: An 8-bit value specifying the supported authentication + type. + + The following enumerated values are supported: + + 0 - Open System + + 1 - WEP Shared Key + + MAC Mode: This field specifies whether the WTP should support the + WLAN in Local or Split MAC mode. Note that the AC MUST NOT + request a mode of operation that was not advertised by the WTP + during the discovery process (see Section 4.6.43 in [RFC5415]). + The following enumerated values are supported: + + 0 - Local MAC: Service for the WLAN is to be provided in Local + MAC mode. + + 1 - Split MAC: Service for the WLAN is to be provided in Split + MAC mode. + + Tunnel Mode: This field specifies the frame tunneling type to be + used for 802.11 data frames from all stations associated with the + WLAN. The AC MUST NOT request a mode of operation that was not + advertised by the WTP during the discovery process (see Section + 4.6.42 in [RFC5415]). All IEEE 802.11 management frames MUST be + tunneled using 802.11 Tunnel mode. The following enumerated + values are supported: + + 0 - Local Bridging: All user traffic is to be locally bridged. + + 1 - 802.3 Tunnel: All user traffic is to be tunneled to the AC + in 802.3 format (see Section 4.4.2 in [RFC5415]). Note that + this option MUST NOT be selected with Split MAC mode. + + 2 - 802.11 Tunnel: All user traffic is to be tunneled to the AC + in 802.11 format. + + Suppress SSID: A boolean indicating whether the SSID is to be + advertised by the WTP. A value of zero suppresses the SSID in the + 802.11 Beacon and Probe Response frames, while a value of one will + cause the WTP to populate the field. + + + + + +Calhoun, et al. Standards Track [Page 34] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + SSID: The SSID attribute is the service set identifier that will be + advertised by the WTP for this WLAN. The SSID field contains any + ASCII character and MUST NOT exceed 32 octets in length, as + defined in [IEEE.802-11.2007]. + +6.2. IEEE 802.11 Antenna + + The IEEE 802.11 Antenna message element is communicated by the WTP to + the AC to provide information on the antennas available. The AC MAY + use this element to reconfigure the WTP's antennas. The message + element contains the following fields: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Diversity | Combiner | Antenna Cnt | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Antenna Selection... + +-+-+-+-+-+-+-+-+ + + Type: 1025 for IEEE 802.11 Antenna + + Length: >= 5 + + Radio ID: An 8-bit value representing the radio to configure, whose + value is between one (1) and 31. + + Diversity: An 8-bit value specifying whether the antenna is to + provide receiver diversity. The value of this field is the same + as the IEEE 802.11 dot11DiversitySelectionRx MIB element, see + [IEEE.802-11.2007]. The following enumerated values are + supported: + + 0 - Disabled + + 1 - Enabled (may only be true if the antenna can be used as a + receiving antenna) + + Combiner: An 8-bit value specifying the combiner selection. The + following enumerated values are supported: + + 1 - Sectorized (Left) + + 2 - Sectorized (Right) + + + + + + + +Calhoun, et al. Standards Track [Page 35] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + 3 - Omni + + 4 - Multiple Input/Multiple Output (MIMO) + + Antenna Count: An 8-bit value specifying the number of Antenna + Selection fields. This value SHOULD be the same as the one found + in the IEEE 802.11 dot11CurrentTxAntenna MIB element (see + [IEEE.802-11.2007]). + + Antenna Selection: One 8-bit antenna configuration value per + antenna in the WTP, containing up to 255 antennas. The following + enumerated values are supported: + + 1 - Internal Antenna + + 2 - External Antenna + +6.3. IEEE 802.11 Assigned WTP BSSID + + The IEEE 802.11 Assigned WTP BSSID is only included by the WTP when + the IEEE 802.11 WLAN Configuration Request included the IEEE 802.11 + Add WLAN message element. The BSSID value field of this message + element contains the BSSID that has been assigned by the WTP, + enabling the WTP to perform its own BSSID assignment. + + The WTP is free to assign the BSSIDs the way it sees fit, but it is + highly recommended that the WTP assign the BSSID using the following + algorithm: BSSID = {base BSSID} + WLAN ID. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | WLAN ID | BSSID + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | BSSID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1026 for IEEE 802.11 Assigned WTP BSSID + + Length: 8 + + Radio ID: An 8-bit value representing the radio, whose value is + between one (1) and 31. + + WLAN ID: An 8-bit value specifying the WLAN Identifier. The value + MUST be between one (1) and 16. + + + + + +Calhoun, et al. Standards Track [Page 36] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + BSSID: The BSSID assigned by the WTP for the WLAN created as a + result of receiving an IEEE 802.11 Add WLAN. + +6.4. IEEE 802.11 Delete WLAN + + The IEEE 802.11 Delete WLAN message element is used to inform the WTP + that a previously created WLAN is to be deleted, and contains the + following fields: + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | WLAN ID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1027 for IEEE 802.11 Delete WLAN + + Length: 2 + + Radio ID: An 8-bit value representing the radio, whose value is + between one (1) and 31. + + WLAN ID: An 8-bit value specifying the WLAN Identifier. The value + MUST be between one (1) and 16. + +6.5. IEEE 802.11 Direct Sequence Control + + The IEEE 802.11 Direct Sequence Control message element is a bi- + directional element. When sent by the WTP, it contains the current + state. When sent by the AC, the WTP MUST adhere to the values + provided. This element is only used for IEEE 802.11b radios. The + message element has the following fields. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Reserved | Current Chan | Current CCA | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Energy Detect Threshold | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1028 for IEEE 802.11 Direct Sequence Control + + Length: 8 + + + + + + + +Calhoun, et al. Standards Track [Page 37] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Radio ID: An 8-bit value representing the radio to configure, whose + value is between one (1) and 31. + + Reserved: All implementations complying with this protocol MUST set + to zero any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all bits + not defined for the version of the protocol they support. + + Current Channel: This attribute contains the current operating + frequency channel of the Direct Sequence Spread Spectrum (DSSS) + PHY. This value comes from the IEEE 802.11 dot11CurrentChannel + MIB element (see [IEEE.802-11.2007]). + + Current CCA: The current Clear Channel Assessment (CCA) method in + operation, whose value can be found in the IEEE 802.11 + dot11CCAModeSupported MIB element (see [IEEE.802-11.2007]). Valid + values are: + + 1 - energy detect only (edonly) + + 2 - carrier sense only (csonly) + + 4 - carrier sense and energy detect (edandcs) + + 8 - carrier sense with timer (cswithtimer) + + 16 - high rate carrier sense and energy detect (hrcsanded) + + Energy Detect Threshold: The current Energy Detect Threshold being + used by the DSSS PHY. The value can be found in the IEEE 802.11 + dot11EDThreshold MIB element (see [IEEE.802-11.2007]). + +6.6. IEEE 802.11 Information Element + + The IEEE 802.11 Information Element is used to communicate any IE + defined in the IEEE 802.11 protocol. The data field contains the raw + IE as it would be included within an IEEE 802.11 MAC management + message. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | WLAN ID |B|P| Reserved |Info Element... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + + + + + +Calhoun, et al. Standards Track [Page 38] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Type: 1029 for IEEE 802.11 Information Element + + Length: >= 4 + + Radio ID: An 8-bit value representing the radio, whose value is + between one (1) and 31. + + WLAN ID: An 8-bit value specifying the WLAN Identifier. The value + MUST be between one (1) and 16. + + B: When set, the WTP is to include the Information Element in IEEE + 802.11 Beacons associated with the WLAN. + + P: When set, the WTP is to include the Information Element in Probe + Responses associated with the WLAN. + + Reserved: All implementations complying with this protocol MUST set + to zero any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all bits + not defined for the version of the protocol they support. + + Info Element: The IEEE 802.11 Information Element, which includes + the type, length, and value field. + +6.7. IEEE 802.11 MAC Operation + + The IEEE 802.11 MAC Operation message element is sent by the AC to + set the IEEE 802.11 MAC parameters on the WTP, and contains the + following fields. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Reserved | RTS Threshold | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Short Retry | Long Retry | Fragmentation Threshold | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Tx MSDU Lifetime | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Rx MSDU Lifetime | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1030 for IEEE 802.11 MAC Operation + + Length: 16 + + + + + + +Calhoun, et al. Standards Track [Page 39] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Radio ID: An 8-bit value representing the radio to configure, whose + value is between one (1) and 31. + + Reserved: All implementations complying with this protocol MUST set + to zero any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all bits + not defined for the version of the protocol they support. + + RTS Threshold: This attribute indicates the number of octets in an + MAC Protocol Data Unit (MPDU), below which a Request To Send/Clear + To Send (RTS/CTS) handshake MUST NOT be performed. An RTS/CTS + handshake MUST be performed at the beginning of any frame exchange + sequence where the MPDU is of type Data or Management, the MPDU + has an individual address in the Address1 field, and the length of + the MPDU is greater than this threshold. Setting this attribute + to be larger than the maximum MSDU size MUST have the effect of + turning off the RTS/CTS handshake for frames of Data or Management + type transmitted by this STA. Setting this attribute to zero MUST + have the effect of turning on the RTS/CTS handshake for all frames + of Data or Management type transmitted by this STA. The default + value of this attribute MUST be 2347. The value of this field + comes from the IEEE 802.11 dot11RTSThreshold MIB element, (see + [IEEE.802-11.2007]). + + Short Retry: This attribute indicates the maximum number of + transmission attempts of a frame, the length of which is less than + or equal to RTSThreshold, that MUST be made before a failure + condition is indicated. The default value of this attribute MUST + be 7. The value of this field comes from the IEEE 802.11 + dot11ShortRetryLimit MIB element, (see [IEEE.802-11.2007]). + + Long Retry: This attribute indicates the maximum number of + transmission attempts of a frame, the length of which is greater + than dot11RTSThreshold, that MUST be made before a failure + condition is indicated. The default value of this attribute MUST + be 4. The value of this field comes from the IEEE 802.11 + dot11LongRetryLimit MIB element, (see [IEEE.802-11.2007]). + + Fragmentation Threshold: This attribute specifies the current + maximum size, in octets, of the MPDU that MAY be delivered to the + PHY. A MAC Service Data Unit (MSDU) MUST be broken into fragments + if its size exceeds the value of this attribute after adding MAC + headers and trailers. An MSDU or MAC Management Protocol Data + Unit (MMPDU) MUST be fragmented when the resulting frame has an + individual address in the Address1 field, and the length of the + frame is larger than this threshold. The default value for this + attribute MUST be the lesser of 2346 or the aMPDUMaxLength of the + attached PHY and MUST never exceed the lesser of 2346 or the + + + +Calhoun, et al. Standards Track [Page 40] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + aMPDUMaxLength of the attached PHY. The value of this attribute + MUST never be less than 256. The value of this field comes from + the IEEE 802.11 dot11FragmentationThreshold MIB element, (see + [IEEE.802-11.2007]). + + Tx MSDU Lifetime: This attribute specifies the elapsed time in Time + Units (TUs), after the initial transmission of an MSDU, after + which further attempts to transmit the MSDU MUST be terminated. + The default value of this attribute MUST be 512. The value of + this field comes from the IEEE 802.11 dot11MaxTransmitMSDULifetime + MIB element, (see [IEEE.802-11.2007]). + + Rx MSDU Lifetime: This attribute specifies the elapsed time in TU, + after the initial reception of a fragmented MMPDU or MSDU, after + which further attempts to reassemble the MMPDU or MSDU MUST be + terminated. The default value MUST be 512. The value of this + field comes from the IEEE 802.11 dot11MaxReceiveLifetime MIB + element, (see [IEEE.802-11.2007]). + +6.8. IEEE 802.11 MIC Countermeasures + + The IEEE 802.11 MIC Countermeasures message element is sent by the + WTP to the AC to indicate the occurrence of a MIC failure. For more + information on MIC failure events, see the + dot11RSNATKIPCounterMeasuresInvoked MIB element definition in + [IEEE.802-11.2007]. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | WLAN ID | MAC Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MAC Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1031 for IEEE 802.11 MIC Countermeasures + + Length: 8 + + Radio ID: The Radio Identifier, whose value is between one (1) and + 31, typically refers to some interface index on the WTP. + + WLAN ID: This 8-bit unsigned integer includes the WLAN Identifier, + on which the MIC failure occurred. The value MUST be between one + (1) and 16. + + + + + + +Calhoun, et al. Standards Track [Page 41] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + MAC Address: The MAC Address of the station that caused the MIC + failure. + +6.9. IEEE 802.11 Multi-Domain Capability + + The IEEE 802.11 Multi-Domain Capability message element is used by + the AC to inform the WTP of regulatory limits. The AC will transmit + one message element per frequency band to indicate the regulatory + constraints in that domain. The message element contains the + following fields. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Reserved | First Channel # | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Number of Channels | Max Tx Power Level | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1032 for IEEE 802.11 Multi-Domain Capability + + Length: 8 + + Radio ID: An 8-bit value representing the radio to configure, whose + value is between one (1) and 31. + + Reserved: All implementations complying with this protocol MUST set + to zero any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all bits + not defined for the version of the protocol they support. + + First Channel #: This attribute indicates the value of the lowest + channel number in the sub-band for the associated domain country + string. The value of this field comes from the IEEE 802.11 + dot11FirstChannelNumber MIB element (see [IEEE.802-11.2007]). + + Number of Channels: This attribute indicates the value of the total + number of channels allowed in the sub-band for the associated + domain country string (see Section 6.23). The value of this field + comes from the IEEE 802.11 dot11NumberofChannels MIB element (see + [IEEE.802-11.2007]). + + Max Tx Power Level: This attribute indicates the maximum transmit + power, in dBm, allowed in the sub-band for the associated domain + country string (see Section 6.23). The value of this field comes + from the IEEE 802.11 dot11MaximumTransmitPowerLevel MIB element + (see [IEEE.802-11.2007]). + + + + +Calhoun, et al. Standards Track [Page 42] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + +6.10. IEEE 802.11 OFDM Control + + The IEEE 802.11 Orthogonal Frequency Division Multiplexing (OFDM) + Control message element is a bi-directional element. When sent by + the WTP, it contains the current state. When sent by the AC, the WTP + MUST adhere to the received values. This message element is only + used for 802.11a radios and contains the following fields: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Reserved | Current Chan | Band Support | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TI Threshold | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1033 for IEEE 802.11 OFDM Control + + Length: 8 + + Radio ID: An 8-bit value representing the radio to configure, whose + value is between one (1) and 31. + + Reserved: All implementations complying with this protocol MUST set + to zero any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all bits + not defined for the version of the protocol they support. + + Current Channel: This attribute contains the current operating + frequency channel of the OFDM PHY. The value of this field comes + from the IEEE 802.11 dot11CurrentFrequency MIB element (see + [IEEE.802-11.2007]). + + Band Supported: The capability of the OFDM PHY implementation to + operate in the three Unlicensed National Information + Infrastructure (U-NII) bands. The value of this field comes from + the IEEE 802.11 dot11FrequencyBandsSupported MIB element (see + [IEEE.802-11.2007]), coded as a bit field, whose values are: + + Bit 0 - capable of operating in the 5.15-5.25 GHz band + + Bit 1 - capable of operating in the 5.25-5.35 GHz band + + Bit 2 - capable of operating in the 5.725-5.825 GHz band + + + + + + + +Calhoun, et al. Standards Track [Page 43] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Bit 3 - capable of operating in the 5.47-5.725 GHz band + + Bit 4 - capable of operating in the lower Japanese 5.25 GHz band + + Bit 5 - capable of operating in the 5.03-5.091 GHz band + + Bit 6 - capable of operating in the 4.94-4.99 GHz band + + For example, for an implementation capable of operating in the + 5.15-5.35 GHz bands, this attribute would take the value 3. + + TI Threshold: The threshold being used to detect a busy medium + (frequency). CCA MUST report a busy medium upon detecting the + RSSI above this threshold. The value of this field comes from the + IEEE 802.11 dot11TIThreshold MIB element (see [IEEE.802-11.2007]). + +6.11. IEEE 802.11 Rate Set + + The rate set message element value is sent by the AC and contains the + supported operational rates. It contains the following fields. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Rate Set... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1034 for IEEE 802.11 Rate Set + + Length: >= 3 + + Radio ID: An 8-bit value representing the radio to configure, whose + value is between one (1) and 31. + + Rate Set: The AC generates the Rate Set that the WTP is to include + in its Beacon and Probe messages. The length of this field is + between 2 and 8 bytes. The value of this field comes from the + IEEE 802.11 dot11OperationalRateSet MIB element (see + [IEEE.802-11.2007]). + +6.12. IEEE 802.11 RSNA Error Report From Station + + The IEEE 802.11 RSN Error Report From Station message element is used + by a WTP to send RSN error reports to the AC. The WTP does not need + to transmit any reports that do not include any failures. The fields + from this message element come from the IEEE 802.11 + Dot11RSNAStatsEntry table, see [IEEE.802-11.2007]. + + + + +Calhoun, et al. Standards Track [Page 44] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Client MAC Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Client MAC Address | BSSID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | BSSID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | WLAN ID | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TKIP ICV Errors | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TKIP Local MIC Failures | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TKIP Remote MIC Failures | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | CCMP Replays | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | CCMP Decrypt Errors | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | TKIP Replays | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + Type: 1035 for IEEE 802.11 RSNA Error Report From Station + + Length: 40 + + Client MAC Address: The Client MAC Address of the station. + + BSSID: The BSSID on which the failures are being reported. + + Radio ID: The Radio Identifier, whose value is between one (1) and + 31, typically refers to some interface index on the WTP. + + WLAN ID: The WLAN ID on which the RSNA failures are being reported. + The value MUST be between one (1) and 16. + + Reserved: All implementations complying with this protocol MUST set + to zero any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all bits + not defined for the version of the protocol they support. + + + + + + + + +Calhoun, et al. Standards Track [Page 45] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + TKIP ICV Errors: A 32-bit value representing the number of Temporal + Key Integrity Protocol (TKIP) (as defined in [IEEE.802-11.2007]) + ICV errors encountered when decrypting packets from the station. + The value of this field comes from the IEEE 802.11 + dot11RSNAStatsTKIPICVErrors MIB element (see [IEEE.802-11.2007]). + + TKIP Local MIC Failures: A 32-bit value representing the number of + MIC failures encountered when checking the integrity of packets + received from the station. The value of this field comes from the + IEEE 802.11 dot11RSNAStatsTKIPLocalMICFailures MIB element (see + [IEEE.802-11.2007]). + + TKIP Remote MIC Failures: A 32-bit value representing the number of + MIC failures reported by the station encountered (possibly via the + EAPOL-Key frame). The value of this field comes from the IEEE + 802.11 dot11RSNAStatsTKIPRemoteMICFailures MIB element (see + [IEEE.802-11.2007]). + + CCMP Replays: A 32-bit value representing the number of CCMP MPDUs + discarded by the replay detection mechanism. The value of this + field comes from the IEEE 802.11 dot11RSNACCMPReplays MIB element + (see [IEEE.802-11.2007]). + + CCMP Decrypt Errors: A 32-bit value representing the number of CCMP + MDPUs discarded by the decryption algorithm. The value of this + field comes from the IEEE 802.11 dot11RSNACCMPDecryptErrors MIB + element (see [IEEE.802-11.2007]). + + TKIP Replays: A 32-bit value representing the number of TKIP + Replays detected in frames received from the station. The value + of this field comes from the IEEE 802.11 dot11RSNAStatsTKIPReplays + MIB element (see [IEEE.802-11.2007]). + +6.13. IEEE 802.11 Station + + The IEEE 802.11 Station message element accompanies the Add Station + message element, and is used to deliver IEEE 802.11 station policy + from the AC to the WTP. + + The latest IEEE 802.11 Station message element overrides any + previously received message elements. + + If the QoS field is set, the WTP MUST observe and provide policing of + the 802.11e priority tag to ensure that it does not exceed the value + provided by the AC. + + + + + + +Calhoun, et al. Standards Track [Page 46] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Association ID | Flags | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MAC Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MAC Address | Capabilities | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | WLAN ID |Supported Rates| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1036 for IEEE 802.11 Station + + Length: >= 14 + + Radio ID: An 8-bit value representing the radio, whose value is + between one (1) and 31. + + Association ID: A 16-bit value specifying the IEEE 802.11 + Association Identifier. + + Flags: All implementations complying with this protocol MUST set to + zero any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all bits + not defined for the version of the protocol they support. + + MAC Address: The station's MAC Address + + Capabilities: A 16-bit field containing the IEEE 802.11 + Capabilities Information Field to use with the station. + + WLAN ID: An 8-bit value specifying the WLAN Identifier. The value + MUST be between one (1) and 16. + + Supported Rates: The variable-length field containing the supported + rates to be used with the station, as found in the IEEE 802.11 + dot11OperationalRateSet MIB element (see [IEEE.802-11.2007]). + This field MUST NOT exceed 126 octets and specifies the set of + data rates at which the station may transmit data, where each + octet represents a data rate. + +6.14. IEEE 802.11 Station QoS Profile + + The IEEE 802.11 Station QoS Profile message element contains the + maximum IEEE 802.11e priority tag that may be used by the station. + Any packet received that exceeds the value encoded in this message + element MUST be tagged using the maximum value permitted by to the + + + +Calhoun, et al. Standards Track [Page 47] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + user. The priority tag MUST be between zero (0) and seven (7). This + message element MUST NOT be present without the IEEE 802.11 Station + (see Section 6.13) message element. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MAC Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MAC Address | Reserved |8021p| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1037 for IEEE 802.11 Station QoS Profile + + Length: 8 + + MAC Address: The station's MAC Address + + Reserved: All implementations complying with this protocol MUST set + to zero any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all bits + not defined for the version of the protocol they support. + + 8021p: The maximum 802.1p priority value that the WTP will allow in + the Traffic Identifier (TID) field in the extended 802.11e QoS + Data header. + +6.15. IEEE 802.11 Station Session Key + + The IEEE 802.11 Station Session Key message element is sent by the AC + to provision encryption keys, or to configure an access policy, on + the WTP. This message element MUST NOT be present without the IEEE + 802.11 Station (see Section 6.13) message element, and MUST NOT be + sent if the WTP had not specifically advertised support for the + requested encryption scheme, through the WTP Descriptor Message + Element's Encryption Capabilities field (see Section 8.1). + + When the Key field is non-zero in length, the RSN Information Element + MUST be sent along with the IEEE 802.11 Station Session Key in order + to instruct the WTP on the usage of the Key field. The WTP MUST + observe the Authentication and Key Management (AKM) field of the RSN + Information Element in order to identify the authentication protocol + to be enforced with the station. + + If cryptographic services are provided at the WTP, the WTP MUST + observe the algorithm dictated in the Pairwise Cipher Suite field of + the RSN Information Element sent by the AC. The RSN Information + Element included here is the one sent by the AC in the third message + + + +Calhoun, et al. Standards Track [Page 48] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + of the 4-Way Key Handshake, which specifies which cipher is to be + applied to provide encryption and decryption services with the + station. The RSN Information Element is used to communicate any + supported algorithm, including WEP, TKIP, and AES-CCMP. In the case + of static WEP keys, the RSN Information Element is still used to + indicate the cryptographic algorithm even though no key exchange + occurred. + + If the IEEE 802.11 Station Session Key message element's 'AKM-Only' + bit is set, the WTP MUST drop all IEEE 802.11 packets that are not + part of the Authentication and Key Management (AKM), such as EAP. + Note that AKM-Only MAY be set while an encryption key is in force, + requiring that the AKM packets be encrypted. Once the station has + successfully completed authentication via the AKM, the AC MUST send a + new Add Station message element to remove the AKM-Only restriction, + and optionally push the session key down to the WTP. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MAC Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MAC Address |A|C| Flags | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pairwise TSC | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pairwise TSC | Pairwise RSC | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pairwise RSC | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Key... + +-+-+-+-+-+-+-+- + + Type: 1038 for IEEE 802.11 Station Session Key + + Length: >= 25 + + MAC Address: The station's MAC Address + + Flags: All implementations complying with this protocol MUST set to + zero any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all bits + not defined for the version of the protocol they support. The + following bits are defined: + + + + + + + +Calhoun, et al. Standards Track [Page 49] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + A: The 1-bit AKM-Only field is set by the AC to inform the WTP + that is MUST NOT accept any 802.11 Data Frames other than AKM + frames. This is the equivalent of the WTP's IEEE 802.1X port + for the station to be in the closed state. When set, the WTP + MUST drop any non-IEEE 802.1X packets it receives from the + station. + + C: The 1-bit field is set by the AC to inform the WTP that + encryption services will be provided by the AC. When set, + the WTP SHOULD police frames received from stations to ensure + that they are properly encrypted as specified in the RSN + Information Element, but does not need to take specific + cryptographic action on the frame. Similarly, for + transmitted frames, the WTP only needs to forward already + encrypted frames. Since packets received by the WTP will be + encrypted, the WTP cannot modify the contents of the packets, + including modifying the DSCP markings of the encapsulated + packet. In this case, this function would be the + responsibility of the AC. + + Pairwise TSC: The 6-byte Transmit Sequence Counter (TSC) field to + use for unicast packets transmitted to the station. + + Pairwise RSC: The 6-byte Receive Sequence Counter (RSC) to use for + unicast packets received from the station. + + Key: The pairwise key the WTP is to use when encrypting traffic to/ + from the station. The format of the keys differs based on the + crypto algorithm used. For unicast WEP keys, the Key field + consists of the actual unicast encryption key (note, this is used + when WEP is used in conjunction with 802.1X, and therefore a + unicast encryption key exists). When used with CCMP, the Key + field includes the 128-bit Temporal Key. When used with TKIP, the + Key field includes the 256-bit Temporal Key (which consists of a + 128-bit key used as input for TKIP key mixing, and two 64-bit keys + used for Michael). + +6.16. IEEE 802.11 Statistics + + The IEEE 802.11 Statistics message element is sent by the WTP to + transmit its current statistics, and it contains the following + fields. All of the fields in this message element are set to zero + upon WTP initialization. The fields will roll over when they reach + their maximum value of 4294967295. Due to the nature of each counter + representing different data points, the rollover event will vary + + + + + + +Calhoun, et al. Standards Track [Page 50] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + greatly across each field. Applications or human operators using + these counters need to be aware of the minimal possible times between + rollover events in order to make sure that no consecutive rollover + events are missed. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Tx Fragment Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Multicast Tx Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Failed Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Retry Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Multiple Retry Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Frame Duplicate Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | RTS Success Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | RTS Failure Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ACK Failure Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Rx Fragment Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Multicast RX Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | FCS Error Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Tx Frame Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Decryption Errors | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Discarded QoS Fragment Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Associated Station Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | QoS CF Polls Received Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | QoS CF Polls Unused Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | QoS CF Polls Unusable Count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + +Calhoun, et al. Standards Track [Page 51] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Type: 1039 for IEEE 802.11 Statistics + + Length: 80 + + Radio ID: An 8-bit value representing the radio, whose value is + between one (1) and 31. + + Reserved: All implementations complying with this protocol MUST set + to zero any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all bits + not defined for the version of the protocol they support. + + Tx Fragment Count: A 32-bit value representing the number of + fragmented frames transmitted. The value of this field comes from + the IEEE 802.11 dot11TransmittedFragmentCount MIB element (see + [IEEE.802-11.2007]). + + Multicast Tx Count: A 32-bit value representing the number of + multicast frames transmitted. The value of this field comes from + the IEEE 802.11 dot11MulticastTransmittedFrameCount MIB element + (see [IEEE.802-11.2007]). + + Failed Count: A 32-bit value representing the transmit excessive + retries. The value of this field comes from the IEEE 802.11 + dot11FailedCount MIB element (see [IEEE.802-11.2007]). + + Retry Count: A 32-bit value representing the number of transmit + retries. The value of this field comes from the IEEE 802.11 + dot11RetryCount MIB element (see [IEEE.802-11.2007]). + + Multiple Retry Count: A 32-bit value representing the number of + transmits that required more than one retry. The value of this + field comes from the IEEE 802.11 dot11MultipleRetryCount MIB + element (see [IEEE.802-11.2007]). + + Frame Duplicate Count: A 32-bit value representing the duplicate + frames received. The value of this field comes from the IEEE + 802.11 dot11FrameDuplicateCount MIB element (see + [IEEE.802-11.2007]). + + RTS Success Count: A 32-bit value representing the number of + successfully transmitted Ready To Send (RTS). The value of this + field comes from the IEEE 802.11 dot11RTSSuccessCount MIB element + (see [IEEE.802-11.2007]). + + + + + + + +Calhoun, et al. Standards Track [Page 52] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + RTS Failure Count: A 32-bit value representing the failed + transmitted RTS. The value of this field comes from the IEEE + 802.11 dot11RTSFailureCount MIB element (see [IEEE.802-11.2007]). + + ACK Failure Count: A 32-bit value representing the number of failed + acknowledgements. The value of this field comes from the IEEE + 802.11 dot11ACKFailureCount MIB element (see [IEEE.802-11.2007]). + + Rx Fragment Count: A 32-bit value representing the number of + fragmented frames received. The value of this field comes from + the IEEE 802.11 dot11ReceivedFragmentCount MIB element (see + [IEEE.802-11.2007]). + + Multicast RX Count: A 32-bit value representing the number of + multicast frames received. The value of this field comes from the + IEEE 802.11 dot11MulticastReceivedFrameCount MIB element (see + [IEEE.802-11.2007]). + + FCS Error Count: A 32-bit value representing the number of FCS + failures. The value of this field comes from the IEEE 802.11 + dot11FCSErrorCount MIB element (see [IEEE.802-11.2007]). + + Decryption Errors: A 32-bit value representing the number of + Decryption errors that occurred on the WTP. Note that this field + is only valid in cases where the WTP provides encryption/ + decryption services. The value of this field comes from the IEEE + 802.11 dot11WEPUndecryptableCount MIB element (see + [IEEE.802-11.2007]). + + Discarded QoS Fragment Count: A 32-bit value representing the + number of discarded QoS fragments received. The value of this + field comes from the IEEE 802.11 dot11QoSDiscardedFragmentCount + MIB element (see [IEEE.802-11.2007]). + + Associated Station Count: A 32-bit value representing the number of + number of associated stations. The value of this field comes from + the IEEE 802.11 dot11AssociatedStationCount MIB element (see + [IEEE.802-11.2007]). + + QoS CF Polls Received Count: A 32-bit value representing the number + of (+)CF-Polls received. The value of this field comes from the + IEEE 802.11 dot11QosCFPollsReceivedCount MIB element (see + [IEEE.802-11.2007]). + + QoS CF Polls Unused Count: A 32-bit value representing the number + of (+)CF-Polls that have been received, but not used. The value + of this field comes from the IEEE 802.11 + dot11QosCFPollsUnusedCount MIB element (see [IEEE.802-11.2007]). + + + +Calhoun, et al. Standards Track [Page 53] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + QoS CF Polls Unusable Count: A 32-bit value representing the number + of (+)CF-Polls that have been received, but could not be used due + to the Transmission Opportunity (TXOP) size being smaller than the + time that is required for one frame exchange sequence. The value + of this field comes from the IEEE 802.11 + dot11QosCFPollsUnusableCount MIB element (see [IEEE.802-11.2007]). + +6.17. IEEE 802.11 Supported Rates + + The IEEE 802.11 Supported Rates message element is sent by the WTP to + indicate the rates that it supports, and contains the following + fields. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Supported Rates... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1040 for IEEE 802.11 Supported Rates + + Length: >= 3 + + Radio ID: An 8-bit value representing the radio, whose value is + between one (1) and 31. + + Supported Rates: The WTP includes the Supported Rates that its + hardware supports. The format is identical to the Rate Set + message element and is between 2 and 8 bytes in length. + +6.18. IEEE 802.11 Tx Power + + The IEEE 802.11 Tx Power message element value is bi-directional. + When sent by the WTP, it contains the current power level of the + radio in question. When sent by the AC, it contains the power level + to which the WTP MUST adhere. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Reserved | Current Tx Power | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1041 for IEEE 802.11 Tx Power + + + + + + + +Calhoun, et al. Standards Track [Page 54] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Length: 4 + + Radio ID: An 8-bit value representing the radio to configure, whose + value is between one (1) and 31. + + Reserved: All implementations complying with this protocol MUST set + to zero any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all bits + not defined for the version of the protocol they support. + + Current Tx Power: This attribute contains the current transmit + output power in mW, as described in the dot11CurrentTxPowerLevel + MIB variable, see [IEEE.802-11.2007]. + +6.19. IEEE 802.11 Tx Power Level + + The IEEE 802.11 Tx Power Level message element is sent by the WTP and + contains the different power levels supported. The values found in + this message element are found in the IEEE 802.11 + Dot11PhyTxPowerEntry MIB table, see [IEEE.802-11.2007]. + + The value field contains the following: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Num Levels | Power Level [n] | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1042 for IEEE 802.11 Tx Power Level + + Length: >= 4 + + Radio ID: An 8-bit value representing the radio to configure, whose + value is between one (1) and 31. + + Num Levels: The number of power level attributes. The value of + this field comes from the IEEE 802.11 + dot11NumberSupportedPowerLevels MIB element (see + [IEEE.802-11.2007]). + + Power Level: Each power level field contains a supported power + level, in mW. The value of this field comes from the + corresponding IEEE 802.11 dot11TxPowerLevel[n] MIB element, see + [IEEE.802-11.2007]. + + + + + + +Calhoun, et al. Standards Track [Page 55] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + +6.20. IEEE 802.11 Update Station QoS + + The IEEE 802.11 Update Station QoS message element is used to change + the Quality of Service policy on the WTP for a given station. The + QoS tags included in this message element are to be applied to + packets received at the WTP from the station indicated through the + MAC Address field. This message element overrides the default values + provided through the IEEE 802.11 WTP Quality of Service message + element (see Section 6.22). Any tagging performed by the WTP MUST be + directly applied to the packets received from the station, as well as + the CAPWAP tunnel, if the packets are tunneled to the AC. See + Section 2.6 for more information. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | MAC Address | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | MAC Address | QoS Sub-Element... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1043 for IEEE 802.11 Update Station QoS + + Length: 8 + + Radio ID: The Radio Identifier, whose value is between one (1) and + 31, typically refers to some interface index on the WTP. + + MAC Address: The station's MAC Address. + + QoS Sub-Element: The IEEE 802.11 WTP Quality of Service message + element contains four QoS sub-elements, one for every QoS profile. + The order of the QoS profiles are Voice, Video, Best Effort, and + Background. + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Reserved|8021p|RSV| DSCP Tag | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + + Reserved: All implementations complying with this protocol MUST + set to zero any bits that are reserved in the version of the + protocol supported by that implementation. Receivers MUST + ignore all bits not defined for the version of the protocol + they support. + + + + +Calhoun, et al. Standards Track [Page 56] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + 8021p: The 3-bit 802.1p priority value to use if packets are to + be IEEE 802.1p tagged. This field is used only if the 'P' bit + in the WTP Quality of Service message element was set; + otherwise, its contents MUST be ignored. + + RSV: All implementations complying with this protocol MUST set + to zero any bits that are reserved in the version of the + protocol supported by that implementation. Receivers MUST + ignore all bits not defined for the version of the protocol + they support. + + DSCP Tag: The 6-bit DSCP label to use if packets are eligible to + be DSCP tagged, specifically an IPv4 or IPv6 packet (see + [RFC2474]). This field is used only if the 'D' bit in the WTP + Quality of Service message element was set; otherwise, its + contents MUST be ignored. + +6.21. IEEE 802.11 Update WLAN + + The IEEE 802.11 Update WLAN message element is used by the AC to + define a wireless LAN on the WTP. The inclusion of this message + element MUST also include the IEEE 802.11 Information Element message + element, containing the following 802.11 IEs: + + Power Constraint information element + + WPA information element [WPA] + + RSN information element + + Enhanced Distributed Channel Access (EDCA) Parameter Set information + element + + QoS Capability information element + + WMM information element [WMM] + + These IEEE 802.11 Information Elements are stored by the WTP and + included in any Probe Responses and Beacons generated, as specified + in the IEEE 802.11 standard [IEEE.802-11.2007]. + + If cryptographic services are provided at the WTP, the WTP MUST + observe the algorithm dictated in the Group Cipher Suite field of the + RSN Information Element sent by the AC. The RSN Information Element + is used to communicate any supported algorithm, including WEP, TKIP, + and AES-CCMP. In the case of static WEP keys, the RSN Information + Element is still used to indicate the cryptographic algorithm even + though no key exchange occurred. + + + +Calhoun, et al. Standards Track [Page 57] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + The message element uses the following format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | WLAN ID | Capability | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Key Index | Key Status | Key Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Key... | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1044 for IEEE 802.11 Update WLAN + + Length: >= 8 + + Radio ID: An 8-bit value representing the radio, whose value is + between one (1) and 31. + + WLAN ID: An 8-bit value specifying the WLAN Identifier. The value + MUST be between one (1) and 16. + + Capability: A 16-bit value containing the Capability information + field to be advertised by the WTP in the Probe Request and Beacon + frames. Each bit of the Capability field represents a different + WTP capability, which are described in detail in + [IEEE.802-11.2007]. The format of the field is: + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |E|I|C|F|P|S|B|A|M|Q|T|D|V|O|K|L| + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + E (ESS): The AC MUST set the Extended Service Set (ESS) subfield + to 1. + + I (IBSS): The AC MUST set the Independent Basic Service Set + (IBSS) subfield to 0. + + C (CF-Pollable): The AC sets the Contention Free Pollable (CF- + Pollable) subfield based on the table found in + [IEEE.802-11.2007]. + + F (CF-Poll Request): The AC sets the CF-Poll Request subfield + based on the table found in [IEEE.802-11.2007]. + + + + + +Calhoun, et al. Standards Track [Page 58] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + P (Privacy): The AC sets the Privacy subfield based on the + confidentiality requirements of the WLAN, as defined in + [IEEE.802-11.2007]. + + S (Short Preamble): The AC sets the Short Preamble subfield + based on whether the use of short preambles are permitted on the + WLAN, as defined in [IEEE.802-11.2007]. + + B (PBCC): The AC sets the Packet Binary Convolutional Code + (PBCC) modulation option subfield based on whether the use of + PBCC is permitted on the WLAN, as defined in [IEEE.802-11.2007]. + + A (Channel Agility): The AC sets the Channel Agility subfield + based on whether the WTP is capable of supporting the High Rate + Direct Sequence Spread Spectrum (HR/DSSS), as defined in + [IEEE.802-11.2007]. + + M (Spectrum Management): The AC sets the Spectrum Management + subfield according to the value of the + dot11SpectrumManagementRequired MIB variable, as defined in + [IEEE.802-11.2007]. + + Q (QoS): The AC sets the Quality of Service (QoS) subfield based + on the table found in [IEEE.802-11.2007]. + + T (Short Slot Time): The AC sets the Short Slot Time subfield + according to the value of the WTP's currently used slot time + value, as defined in [IEEE.802-11.2007]. + + D (APSD): The AC sets the APSD subfield according to the value + of the dot11APSDOptionImplemented Management Information Base + (MIB) variable, as defined in [IEEE.802-11.2007]. + + V (Reserved): The AC sets the Reserved subfield to zero, as + defined in [IEEE.802-11.2007]. + + O (DSSS-OFDM): The AC sets the DSSS-OFDM subfield to indicate + the use of Direct Sequence Spread Spectrum with Orthogonal + Frequency Division Multiplexing (DSSS-OFDM), as defined in + [IEEE.802-11.2007]. + + K (Delayed Block ACK): The AC sets the Delayed Block ACK + subfield according to the value of the + dot11DelayedBlockAckOptionImplemented MIB variable, as defined + in [IEEE.802-11.2007]. + + + + + + +Calhoun, et al. Standards Track [Page 59] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + L (Immediate Block ACK): The AC sets the Delayed Block ACK + subfield according to the value of the + dot11ImmediateBlockAckOptionImplemented MIB variable, as defined + in [IEEE.802-11.2007]. + + Key-Index: The Key-Index associated with the key. + + Key Status: A 1-byte value that specifies the state and usage of + the key that has been included. The following values describe the + key usage and its status: + + 0 - A value of zero, with the inclusion of the RSN Information + Element means that the WLAN uses per-station encryption keys, + and therefore the key in the 'Key' field is only used for + multicast traffic. + + 1 - When set to one, the WLAN employs a shared WEP key, also + known as a static WEP key, and uses the encryption key for + both unicast and multicast traffic for all stations. + + 2 - The value of 2 indicates that the AC will begin rekeying the + GTK with the STA's in the BSS. It is only valid when IEEE + 802.11 is enabled as the security policy for the BSS. + + 3 - The value of 3 indicates that the AC has completed rekeying + the GTK and broadcast packets no longer need to be duplicated + and transmitted with both GTK's. + + Key Length: A 16-bit value representing the length of the Key + field. + + Key: A Session Key, whose length is known via the Key Length field, + used to provide data privacy. For static WEP keys, which is true + when the 'Key Status' bit is set to one, this key is used for both + unicast and multicast traffic. For encryption schemes that employ + a separate encryption key for unicast and multicast traffic, the + key included here only applies to multicast data, and the cipher + suite is specified in an accompanied RSN Information Element. In + these scenarios, the key, and cipher information, is communicated + via the Add Station message element, see Section 4.6.8 in + [RFC5415]. When used with WEP, the Key field includes the + broadcast key. When used with CCMP, the Key field includes the + 128-bit Group Temporal Key. When used with TKIP, the Key field + includes the 256-bit Group Temporal Key (which consists of a 128- + bit key used as input for TKIP key mixing, and two 64-bit keys + used for Michael). + + + + + +Calhoun, et al. Standards Track [Page 60] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + +6.22. IEEE 802.11 WTP Quality of Service + + The IEEE 802.11 WTP Quality of Service message element value is sent + by the AC to the WTP to communicate Quality of Service configuration + information. The QoS tags included in this message element are the + default QoS values to be applied to packets received by the WTP from + stations on a particular radio. Any tagging performed by the WTP + MUST be directly applied to the packets received from the station, as + well as the CAPWAP tunnel, if the packets are tunneled to the AC. + See Section 2.6 for more information. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID |Tagging Policy | QoS Sub-Element ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1045 for IEEE 802.11 WTP Quality of Service + + Length: 34 + + Radio ID: The Radio Identifier, whose value is between one (1) and + 31, typically refers to some interface index on the WTP. + + Tagging Policy: A bit field indicating how the WTP is to mark + packets for QoS purposes. The required WTP behavior is defined in + Section 2.6.1. The field has the following format: + + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |Rsvd |P|Q|D|O|I| + +-+-+-+-+-+-+-+-+ + + Rsvd: A set of reserved bits for future use. All implementations + complying with this protocol MUST set to zero any bits that are + reserved in the version of the protocol supported by that + implementation. Receivers MUST ignore all bits not defined for + the version of the protocol they support. + + P: When set, the WTP is to employ the 802.1p QoS mechanism (see + Section 2.6.1.1), and the WTP is to use the 'Q' bit. + + Q: When the 'P' bit is set, the 'Q' bit is used by the AC to + communicate to the WTP how 802.1p QoS is to be enforced. + Details on the behavior of the 'Q' bit are specified in + Section 2.6.1.1. + + + + + +Calhoun, et al. Standards Track [Page 61] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + D: When set, the WTP is to employ the DSCP QoS mechanism (see + Section 2.6.1.2), and the WTP is to use the 'O' and 'I' bits. + + O: When the 'D' bit is set, the 'O' bit is used by the AC to + communicate to the WTP how DSCP QoS is to be enforced on the + outer (tunneled) header. Details on the behavior of the 'O' + bit are specified in Section 2.6.1.2. + + I: When the 'D' bit is set, the 'I' bit is used by the AC to + communicate to the WTP how DSCP QoS is to be enforced on the + station's packet (inner) header. Details on the behavior of + the 'I' bit are specified in Section 2.6.1.2. + + QoS Sub-Element: The IEEE 802.11 WTP Quality of Service message + element contains four QoS sub-elements, one for every QoS profile. + The order of the QoS profiles are Voice, Video, Best Effort, and + Background. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Queue Depth | CWMin | CWMax | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | CWMax | AIFS | Reserved|8021p|RSV| DSCP Tag | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Queue Depth: The number of packets that can be on the specific + QoS transmit queue at any given time. + + CWMin: The Contention Window minimum (CWmin) value for the QoS + transmit queue. The value of this field comes from the IEEE + 802.11 dot11EDCATableCWMin MIB element (see + [IEEE.802-11.2007]). + + CWMax: The Contention Window maximum (CWmax) value for the QoS + transmit queue. The value of this field comes from the IEEE + 802.11 dot11EDCATableCWMax MIB element (see + [IEEE.802-11.2007]). + + AIFS: The Arbitration Inter Frame Spacing (AIFS) to use for the + QoS transmit queue. The value of this field comes from the + IEEE 802.11 dot11EDCATableAIFSN MIB element (see + [IEEE.802-11.2007]). + + + + + + + + +Calhoun, et al. Standards Track [Page 62] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Reserved: All implementations complying with this protocol MUST + set to zero any bits that are reserved in the version of the + protocol supported by that implementation. Receivers MUST + ignore all bits not defined for the version of the protocol + they support. + + 8021p: The 3-bit 802.1p priority value to use if packets are to + be IEEE 802.1p tagged. This field is used only if the 'P' bit + is set; otherwise, its contents MUST be ignored. + + RSV: All implementations complying with this protocol MUST set + to zero any bits that are reserved in the version of the + protocol supported by that implementation. Receivers MUST + ignore all bits not defined for the version of the protocol + they support. + + DSCP Tag: The 6-bit DSCP label to use if packets are eligible to + be DSCP tagged, specifically an IPv4 or IPv6 packet (see + [RFC2474]). This field is used only if the 'D' bit is set; + otherwise, its contents MUST be ignored. + +6.23. IEEE 802.11 WTP Radio Configuration + + The IEEE 802.11 WTP WLAN Radio Configuration message element is used + by the AC to configure a Radio on the WTP, and by the WTP to deliver + its radio configuration to the AC. The message element value + contains the following fields: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID |Short Preamble| Num of BSSIDs | DTIM Period | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | BSSID | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | BSSID | Beacon Period | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Country String | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1046 for IEEE 802.11 WTP WLAN Radio Configuration + + Length: 16 + + Radio ID: An 8-bit value representing the radio to configure, whose + value is between one (1) and 31. + + + + + +Calhoun, et al. Standards Track [Page 63] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Short Preamble: An 8-bit value indicating whether short preamble is + supported. The following enumerated values are currently + supported: + + 0 - Short preamble not supported. + + 1 - Short preamble is supported. + + BSSID: The WLAN Radio's base MAC Address. + + Number of BSSIDs: This attribute contains the maximum number of + BSSIDs supported by the WTP. This value restricts the number of + logical networks supported by the WTP, and is between 1 and 16. + + DTIM Period: This attribute specifies the number of Beacon + intervals that elapse between transmission of Beacons frames + containing a Traffic Indication Map (TIM) element whose Delivery + Traffic Indication Message (DTIM) Count field is 0. This value is + transmitted in the DTIM Period field of Beacon frames. The value + of this field comes from the IEEE 802.11 dot11DTIMPeriod MIB + element (see [IEEE.802-11.2007]). + + Beacon Period: This attribute specifies the number of Time Unit + (TU) that a station uses for scheduling Beacon transmissions. + This value is transmitted in Beacon and Probe Response frames. + The value of this field comes from the IEEE 802.11 + dot11BeaconPeriod MIB element (see [IEEE.802-11.2007]). + + Country String: This attribute identifies the country in which the + station is operating. The value of this field comes from the IEEE + 802.11 dot11CountryString MIB element (see [IEEE.802-11.2007]). + Some regulatory domains do not allow WTPs to have user + configurable country string, and require that it be a fixed value + during the manufacturing process. Therefore, WTP vendors that + wish to allow for the configuration of this field will need to + validate this behavior during its radio certification process. + Other WTP vendors may simply wish to treat this WTP configuration + parameter as read-only. The country strings can be found in + [ISO.3166-1]. + + The WTP and AC MAY ignore the value of this field, depending upon + regulatory requirements, for example to avoid classification as a + Software-Defined Radio. When this field is used, the first two + octets of this string is the two-character country string as + described in [ISO.3166-1], and the third octet MUST either be a + space, 'O', 'I', or X' as defined below. When the value of the + + + + + +Calhoun, et al. Standards Track [Page 64] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + third octet is 255 (HEX 0xff), the country string field is not + used, and MUST be ignored. The following are the possible values + for the third octet: + + 1. an ASCII space character, if the regulations under which the + station is operating encompass all environments in the + country, + + 2. an ASCII 'O' character, if the regulations under which the + station is operating are for an outdoor environment only, or + + 3. an ASCII 'I' character, if the regulations under which the + station is operating are for an indoor environment only, + + 4. an ASCII 'X' character, if the station is operating under a + non-country entity. The first two octets of the non-country + entity shall be two ASCII 'XX' characters, + + 5. a HEX 0xff character means that the country string field is + not used and MUST be ignored. + + Note that the last byte of the Country String MUST be set to NULL. + +6.24. IEEE 802.11 WTP Radio Fail Alarm Indication + + The IEEE 802.11 WTP Radio Fail Alarm Indication message element is + sent by the WTP to the AC when it detects a radio failure. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Type | Status | Pad | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Type: 1047 for IEEE 802.11 WTP Radio Fail Alarm Indication + + Length: 4 + + Radio ID: The Radio Identifier, whose value is between one (1) and + 31, typically refers to some interface index on the WTP. + + Type: The type of radio failure detected. The following enumerated + values are supported: + + 1 - Receiver + + 2 - Transmitter + + + + +Calhoun, et al. Standards Track [Page 65] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Status: An 8-bit boolean indicating whether the radio failure is + being reported or cleared. A value of zero is used to clear the + event, while a value of one is used to report the event. + + Pad: All implementations complying with version zero of this + protocol MUST set these bits to zero. Receivers MUST ignore all + bits not defined for the version of the protocol they support. + +6.25. IEEE 802.11 WTP Radio Information + + The IEEE 802.11 WTP Radio Information message element is used to + communicate the radio information for each IEEE 802.11 radio in the + WTP. The Discovery Request message, Primary Discovery Request + message, and Join Request message MUST include one such message + element per radio in the WTP. The Radio-Type field is used by the AC + in order to determine which IEEE 802.11 technology specific binding + is to be used with the WTP. + + The message element contains two fields, as shown below. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio ID | Radio Type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Radio Type | + +-+-+-+-+-+-+-+-+ + + Type: 1048 for IEEE 802.11 WTP Radio Information + + Length: 5 + + Radio ID: The Radio Identifier, whose value is between one (1) and + 31, which typically refers to an interface index on the WTP. + + Radio Type: The type of radio present. Note this is a bit field + that is used to specify support for more than a single type of + PHY/MAC. The field has the following format: + + 0 1 2 3 4 5 6 7 + +-+-+-+-+-+-+-+-+ + |Reservd|N|G|A|B| + +-+-+-+-+-+-+-+-+ + + + + + + + + +Calhoun, et al. Standards Track [Page 66] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + Reservd: A set of reserved bits for future use. All + implementations complying with this protocol MUST set to zero + any bits that are reserved in the version of the protocol + supported by that implementation. Receivers MUST ignore all + bits not defined for the version of the protocol they support. + + N: An IEEE 802.11n radio. + + G: An IEEE 802.11g radio. + + A: An IEEE 802.11a radio. + + B: An IEEE 802.11b radio. + +7. IEEE 802.11 Binding WTP Saved Variables + + This section contains the IEEE 802.11 binding specific variables that + SHOULD be saved in non-volatile memory on the WTP. + +7.1. IEEE80211AntennaInfo + + The WTP-per-radio antenna configuration, defined in Section 6.2. + +7.2. IEEE80211DSControl + + The WTP-per-radio Direct Sequence Control configuration, defined in + Section 6.5. + +7.3. IEEE80211MACOperation + + The WTP-per-radio MAC Operation configuration, defined in + Section 6.7. + +7.4. IEEE80211OFDMControl + + The WTP-per-radio OFDM MAC Operation configuration, defined in + Section 6.10. + +7.5. IEEE80211Rateset + + The WTP-per-radio Basic Rate Set configuration, defined in + Section 6.11. + +7.6. IEEE80211TxPower + + The WTP-per-radio Transmit Power configuration, defined in + Section 6.18. + + + + +Calhoun, et al. Standards Track [Page 67] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + +7.7. IEEE80211QoS + + The WTP-per-radio Quality of Service configuration, defined in + Section 6.22. + +7.8. IEEE80211RadioConfig + + The WTP-per-radio Radio Configuration, defined in Section 6.23. + +8. Technology Specific Message Element Values + + This section lists IEEE 802.11-specific values for the generic CAPWAP + message elements that include fields whose values are technology + specific. + +8.1. WTP Descriptor Message Element, Encryption Capabilities Field + + This specification defines two new bits for the WTP Descriptor's + Encryption Capabilities field, as defined in [RFC5415]. Note that + only the bits defined in this specification are described below. WEP + is not explicitly advertised as a WTP capability since all WTPs are + expected to support the encryption cipher. The format of the + Encryption Capabilities field is: + + 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | |A|T| | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + A: WTP supports AES-CCMP, as defined in [IEEE.802-11.2007]. + + T: WTP supports TKIP and Michael, as defined in [IEEE.802-11.2007] + and [WPA], respectively. + +9. Security Considerations + + This section describes security considerations for using IEEE 802.11 + with the CAPWAP protocol. A complete threat analysis of the CAPWAP + protocol can also be found in [RFC5418]. + +9.1. IEEE 802.11 Security + + When used with an IEEE 802.11 infrastructure with WEP encryption, the + CAPWAP protocol does not add any new vulnerabilities. Derived + Session Keys between the STA and WTP can be compromised, resulting in + + + + + +Calhoun, et al. Standards Track [Page 68] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + many well-documented attacks. Implementers SHOULD discourage the use + of WEP and encourage the use of technically-sound cryptographic + solutions such as those in an IEEE 802.11 RSN. + + STA authentication is performed using IEEE 802.lX, and consequently + EAP. Implementers SHOULD use EAP methods meeting the requirements + specified [RFC4017]. + + When used with IEEE 802.11 RSN security, the CAPWAP protocol may + introduce new vulnerabilities, depending on whether the link security + (packet encryption and integrity verification) is provided by the WTP + or the AC. When the link security function is provided by the AC, no + new security concerns are introduced. + + However, when the WTP provides link security, a new vulnerability + will exist when the following conditions are true: + + o The client is not the first to associate to the WTP/ESSID (i.e., + other clients are associated), a GTK already exists, and + + o traffic has been broadcast under the existing GTK. + + Under these circumstances, the receive sequence counter (KeyRSC) + associated with the GTK is non-zero, but because the AC anchors the + 4-way handshake with the client, the exact value of the KeyRSC is not + known when the AC constructs the message containing the GTK. The + client will update its Key RSC value to the current valid KeyRSC upon + receipt of a valid multicast/broadcast message, but prior to this, + previous multicast/broadcast traffic that was secured with the + existing GTK may be replayed, and the client will accept this traffic + as valid. + + Typically, busy networks will produce numerous multicast or broadcast + frames per second, so the window of opportunity with respect to such + replay is expected to be very small. In most conditions, it is + expected that replayed frames could be detected (and logged) by the + WTP. + + The only way to completely close this window is to provide the exact + KeyRSC value in message 3 of the 4-way handshake; any other approach + simply narrows the window to varying degrees. Given the low relative + threat level this presents, the additional complexity introduced by + providing the exact KeyRSC value is not warranted. That is, this + specification provides for a calculated risk in this regard. + + + + + + + +Calhoun, et al. Standards Track [Page 69] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + The AC SHOULD use an RSC of 0 when computing message-3 of the 4-way + 802.11i handshake, unless the AC has knowledge of a more optimal RSC + value to use. Mechanisms for determining a more optimal RSC value + are outside the scope of this specification. + +10. IANA Considerations + + This section details the actions IANA has taken per this + specification. There are numerous registries that have been be + created, and the contents, document action (see [RFC5226], and + registry format are all included below. Note that in cases where bit + fields are referred to, the bit numbering is left to right, where the + leftmost bit is labeled as bit zero (0). + +10.1. CAPWAP Wireless Binding Identifier + + This specification requires a value assigned from the Wireless + Binding Identifier namespace, defined in [RFC5415]. (1) has been + assigned (see Section 2.1, as it is used in implementations. + +10.2. CAPWAP IEEE 802.11 Message Types + + IANA created a new sub-registry in the existing CAPWAP Message Type + registry, which is defined in [RFC5415]. + + IANA created and maintains the CAPWAP IEEE 802.11 Message Types + sub-registry for all message types whose Enterprise Number is set to + 13277. The namespace is 8 bits (3398912-3399167), where the value + 3398912 is reserved and must not be assigned. The values 3398913 and + 3398914 are allocated in this specification, and can be found in + Section 3. Any new assignments of a CAPWAP IEEE 802.11 Message Type + (whose Enterprise Number is set to 13277) require an Expert Review. + The format of the registry maintained by IANA is as follows: + + CAPWAP IEEE 802.11 Message Type Reference + Control Message Value + +10.3. CAPWAP Message Element Type + + This specification defines new values to be registered to the + existing CAPWAP Message Element Type registry, defined in [RFC5415]. + The values used in this document, 1024 through 1048, as listed in + Figure 8 are recommended as implementations already exist that make + use of these values. + + + + + + + +Calhoun, et al. Standards Track [Page 70] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + +10.4. IEEE 802.11 Key Status + + The Key Status field in the IEEE 802.11 Add WLAN message element (see + Section 6.1) and IEEE 802.11 Update WLAN message element (see + Section 6.21) is used to provide information about the status of the + keying exchange. This document defines four values, zero (0) through + three (3), and the remaining values (4-255) are controlled and + maintained by IANA and requires an Expert Review. + +10.5. IEEE 802.11 QoS + + The QoS field in the IEEE 802.11 Add WLAN message element (see + Section 6.1) is used to configure a QoS policy for the WLAN. The + namespace is 8 bits (0-255), where the values zero (0) through three + (3) are allocated in this specification, and can be found in + Section 6.1. This namespace is managed by IANA and assignments + require an Expert Review. IANA created the IEEE 802.11 QoS registry, + whose format is: + + IEEE 802.11 QoS Type Value Reference + +10.6. IEEE 802.11 Auth Type + + The Auth Type field in the IEEE 802.11 Add WLAN message element (see + Section 6.1) is 8 bits and is used to configure the IEEE 802.11 + authentication policy for the WLAN. The namespace is 8 bits (0-255), + where the values zero (0) and one (1) are allocated in this + specification, and can be found in Section 6.1. This namespace is + managed by IANA and assignments require an Expert Review. IANA + created the IEEE 802.11 Auth Type registry, whose format is: + + IEEE 802.11 Auth Type Type Value Reference + +10.7. IEEE 802.11 Antenna Combiner + + The Combiner field in the IEEE 802.11 Antenna message element (see + Section 6.2) is used to provide information about the WTP's antennas. + The namespace is 8 bits (0-255), where the values one (1) through + four (4) are allocated in this specification, and can be found in + Section 6.2. This namespace is managed by IANA and assignments + require an Expert Review. IANA created the IEEE 802.11 Antenna + Combiner registry, whose format is: + + IEEE 802.11 Antenna Combiner Type Value Reference + + + + + + + +Calhoun, et al. Standards Track [Page 71] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + +10.8. IEEE 802.11 Antenna Selection + + The Antenna Selection field in the IEEE 802.11 Antenna message + element (see Section 6.2) is used to provide information about the + WTP's antennas. The namespace is 8 bits (0-255), where the values + zero (0) is reserved and used and the values one (1) through two (2) + are allocated in this specification, and can be found in Section 6.2. + This namespace is managed by IANA and assignments require an Expert + Review. IANA created the IEEE 802.11 Antenna Selection registry, + whose format is: + + IEEE 802.11 Antenna Selection Type Value Reference + +10.9. IEEE 802.11 Session Key Flags + + The flags field in the IEEE 802.11 Station Session Key message + element (see Section 6.15) is 16 bits and is used to configure the + session key association with the mobile device. This specification + defines bits zero (0) and one (1), while bits two (2) through fifteen + are reserved. The reserved bits are managed by IANA and assignment + requires an Expert Review. IANA created the IEEE 802.11 Session Key + Flags registry, whose format is: + + IEEE 802.11 Station Session Key Bit Position Reference + +10.10. IEEE 802.11 Tagging Policy + + The Tagging Policy field in the IEEE 802.11 WTP Quality of Service + message element (see Section 6.22) is 8 bits and is used to specify + how the CAPWAP Data Channel packets are to be tagged. This + specification defines bits three (3) through seven (7). The + remaining bits are managed by IANA and assignment requires an Expert + Review. IANA created the IEEE 802.11 Tagging Policy registry, whose + format is: + + IEEE 802.11 Tagging Policy Bit Position Reference + +10.11. IEEE 802.11 WTP Radio Fail + + The Type field in the IEEE 802.11 WTP Radio Fail Alarm Indication + message element (see Section 6.24) is used to provide information on + why a WTP's radio has failed. The namespace is 8 bits (0-255), where + the value zero (0) is reserved and unused, while the values one (1) + and two (2) are allocated in this specification, and can be found in + Section 6.24. This namespace is managed by IANA and assignments + require an Expert Review. IANA created the IEEE 802.11 WTP Radio + Fail registry, whose format is: + + + + +Calhoun, et al. Standards Track [Page 72] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + IEEE 802.11 WTP Radio Fail Type Value Reference + +10.12. IEEE 802.11 WTP Radio Type + + The Radio Type field in the IEEE 802.11 WTP Radio Information message + element (see Section 6.25) is 8 bits and is used to provide + information about the WTP's radio type. This specification defines + bits four (4) through seven (7). The remaining bits are managed by + IANA and assignment requires an Expert Review. IANA created the IEEE + 802.11 WTP Radio Type registry, whose format is: + + IEEE 802.11 WTP Radio Type Bit Position Reference + +10.13. WTP Encryption Capabilities + + The WTP Encryption Capabilities field in the WTP Descriptor message + element (see Section 8.1) is 16 bits and is used by the WTP to + indicate its IEEE 802.11 encryption capabilities. This specification + defines bits 12 and 13. The reserved bits are managed by IANA and + assignment requires an Expert Review. IANA created the IEEE 802.11 + Encryption Capabilities registry, whose format is: + + IEEE 802.11 Encryption Capabilities Bit Position Reference + +11. Acknowledgments + + The following individuals are acknowledged for their contributions to + this binding specification: Puneet Agarwal, Charles Clancy, Pasi + Eronen, Saravanan Govindan, Scott Kelly, Peter Nilsson, Bob O'Hara, + David Perkins, Margaret Wasserman, and Yong Zhang. + +12. References + +12.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to + Indicate Requirement Levels", BCP 14, RFC 2119, + March 1997. + + [RFC2474] Nichols, K., Blake, S., Baker, F., and D. Black, + "Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers", + RFC 2474, December 1998. + + [RFC3246] Davie, B., Charny, A., Bennet, J., Benson, K., Le + Boudec, J., Courtney, W., Davari, S., Firoiu, V., + and D. Stiliadis, "An Expedited Forwarding PHB + (Per-Hop Behavior)", RFC 3246, March 2002. + + + +Calhoun, et al. Standards Track [Page 73] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + [RFC3168] Ramakrishnan, K., Floyd, S., and D. Black, "The + Addition of Explicit Congestion Notification + (ECN) to IP", RFC 3168, September 2001. + + [RFC3748] Aboba, B., Blunk, L., Vollbrecht, J., Carlson, + J., and H. Levkowetz, "Extensible Authentication + Protocol (EAP)", RFC 3748, June 2004. + + [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for + Writing an IANA Considerations Section in RFCs", + BCP 26, RFC 5226, May 2008. + + [FIPS.197.2001] National Institute of Standards and Technology, + "Advanced Encryption Standard (AES)", FIPS PUB + 197, November 2001, . + + [ISO.3166-1] ISO Standard, "International Organization for + Standardization, Codes for the representation of + names of countries and their subdivisions - Part + 1: Country codes", ISO Standard 3166-1:1997, + 1997. + + [IEEE.802-11.2007] "Information technology - Telecommunications and + information exchange between systems - Local and + metropolitan area networks - Specific + requirements - Part 11: Wireless LAN Medium + Access Control (MAC) and Physical Layer (PHY) + specifications", IEEE Standard 802.11, 2007, + . + + [RFC5415] Montemurro, M., Stanley, D., and P. Calhoun, + "CAPWAP Protocol Specification", RFC 5415, March + 2009. + + [IEEE.802-1X.2004] "Information technology - Telecommunications and + information exchange between systems - Local and + metropolitan area networks - Specific + requirements - Port-Based Network Access + Control", IEEE Standard 802.1X, 2004, . + + + + + + + + +Calhoun, et al. Standards Track [Page 74] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + + [IEEE.802-1Q.2005] "Information technology - Telecommunications and + information exchange between systems - Local and + metropolitan area networks - Specific + requirements - Virtual Bridged Local Area + Networks", IEEE Standard 802.1Q, 2005, . + +12.2. Informative References + + [RFC4017] Stanley, D., Walker, J., and B. Aboba, + "Extensible Authentication Protocol (EAP) Method + Requirements for Wireless LANs", RFC 4017, + March 2005. + + [RFC4118] Yang, L., Zerfos, P., and E. Sadot, "Architecture + Taxonomy for Control and Provisioning of Wireless + Access Points (CAPWAP)", RFC 4118, June 2005. + + [RFC5418] Kelly, S. and C. Clancy, "Control And + Provisioning for Wireless Access Points (CAPWAP) + Threat Analysis for IEEE 802.11 Deployments", + RFC 5418, March 2009. + + [WPA] "Deploying Wi-Fi Protected Access (WPA) and WPA2 + in the Enterprise", March 2005, . + + [WMM] "Support for Multimedia Applications with Quality + of Service in WiFi Networks)", September 2004, + . + + + + + + + + + + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 75] + +RFC 5416 CAPWAP Protocol Binding for IEEE 802.11 March 2009 + + +Editors' Addresses + + Pat R. Calhoun (editor) + Cisco Systems, Inc. + 170 West Tasman Drive + San Jose, CA 95134 + + Phone: +1 408-902-3240 + EMail: pcalhoun@cisco.com + + + Michael P. Montemurro (editor) + Research In Motion + 5090 Commerce Blvd + Mississauga, ON L4W 5M4 + Canada + + Phone: +1 905-629-4746 x4999 + EMail: mmontemurro@rim.com + + + Dorothy Stanley (editor) + Aruba Networks + 1322 Crossman Ave + Sunnyvale, CA 94089 + + Phone: +1 630-363-1389 + EMail: dstanley@arubanetworks.com + + + + + + + + + + + + + + + + + + + + + + + +Calhoun, et al. Standards Track [Page 76] + diff --git a/src/ac/ac.c b/src/ac/ac.c new file mode 100644 index 0000000..07620cb --- /dev/null +++ b/src/ac/ac.c @@ -0,0 +1,655 @@ +#include "ac.h" +#include "capwap_dtls.h" + +#include + +#ifndef CAPWAP_MULTITHREADING_ENABLE +#error "AC request multithreading\n" +#endif + +struct ac_t g_ac; + +#define AC_STANDARD_NAME "Unknown AC" + +/* Local param */ +static char g_configurationfile[260] = AC_DEFAULT_CONFIGURATION_FILE; + +/* Alloc AC */ +static int ac_init(void) { + /* Network */ + capwap_network_init(&g_ac.net); + g_ac.mtu = CAPWAP_MTU_DEFAULT; + g_ac.binding = capwap_array_create(sizeof(unsigned short), 0); + + /* Standard name */ + strcpy(g_ac.acname.name, AC_STANDARD_NAME); + + /* Descriptor */ + g_ac.descriptor.stationlimit = AC_DEFAULT_MAXSTATION; + g_ac.descriptor.wtplimit = AC_DEFAULT_MAXSESSIONS; + g_ac.descriptor.security = 0; + g_ac.descriptor.rmacfield = CAPWAP_ACDESC_RMACFIELD_NOTSUPPORTED; + g_ac.descriptor.dtlspolicy = CAPWAP_ACDESC_CLEAR_DATA_CHANNEL_ENABLED; + g_ac.descriptor.descsubelement = capwap_array_create(sizeof(struct capwap_acdescriptor_desc_subelement), 0); + + /* */ + g_ac.dfa.ecn.flag = CAPWAP_LIMITED_ECN_SUPPORT; + g_ac.dfa.transport.type = CAPWAP_UDP_TRANSPORT; + + /* */ + g_ac.dfa.timers.discovery = AC_DEFAULT_DISCOVERY_INTERVAL; + g_ac.dfa.timers.echorequest = AC_DEFAULT_ECHO_INTERVAL; + g_ac.dfa.decrypterrorreport_interval = AC_DEFAULT_DECRYPT_ERROR_PERIOD_INTERVAL; + g_ac.dfa.idletimeout.timeout = AC_DEFAULT_IDLE_TIMEOUT_INTERVAL; + g_ac.dfa.wtpfallback.mode = AC_DEFAULT_WTP_FALLBACK_MODE; + + /* */ + g_ac.dfa.acipv4list = capwap_array_create(sizeof(struct capwap_acipv4list_element), 0); + g_ac.dfa.acipv6list = capwap_array_create(sizeof(struct capwap_acipv6list_element), 0); + + /* */ + g_ac.dfa.rfcWaitJoin = AC_DEFAULT_WAITJOIN_INTERVAL; + g_ac.dfa.rfcWaitDTLS = AC_DEFAULT_WAITDTLS_INTERVAL; + g_ac.dfa.rfcChangeStatePendingTimer = AC_DEFAULT_CHANGE_STATE_PENDING_TIMER; + g_ac.dfa.rfcDataCheckTimer = AC_DEFAULT_DATA_CHECK_TIMER; + + /* Sessions */ + capwap_event_init(&g_ac.changesessionlist); + g_ac.sessions = capwap_list_create(); + capwap_lock_init(&g_ac.sessionslock); + g_ac.datasessionshandshake = capwap_list_create(); + + return 1; +} + +/* Destroy AC */ +static void ac_destroy(void) { + /* Dtls */ + capwap_crypt_freecontext(&g_ac.dtlscontext); + + /* */ + capwap_array_free(g_ac.descriptor.descsubelement); + capwap_array_free(g_ac.binding); + + /* */ + capwap_array_free(g_ac.dfa.acipv4list); + capwap_array_free(g_ac.dfa.acipv6list); + + /* Sessions */ + capwap_list_free(g_ac.sessions); + capwap_lock_destroy(&g_ac.sessionslock); + capwap_event_destroy(&g_ac.changesessionlist); + capwap_list_free(g_ac.datasessionshandshake); +} + +/* Help */ +static void ac_print_usage(void) { +} + +/* Parsing configuration */ +static int ac_parsing_configuration_1_0(config_t* config) { + int i; + int configInt; + int configIPv4; + int configIPv6; + long int configLongInt; + const char* configString; + config_setting_t* configSetting; + + /* Logging configuration */ + if (config_lookup_bool(config, "logging.enable", &configInt) == CONFIG_TRUE) { + if (!configInt) { + capwap_logging_verboselevel(CAPWAP_LOGGING_NONE); + capwap_logging_disable_allinterface(); + } else { + if (config_lookup_string(config, "logging.level", &configString) == CONFIG_TRUE) { + if (!strcmp(configString, "fatal")) { + capwap_logging_verboselevel(CAPWAP_LOGGING_FATAL); + } else if (!strcmp(configString, "error")) { + capwap_logging_verboselevel(CAPWAP_LOGGING_ERROR); + } else if (!strcmp(configString, "warning")) { + capwap_logging_verboselevel(CAPWAP_LOGGING_WARNING); + } else if (!strcmp(configString, "info")) { + capwap_logging_verboselevel(CAPWAP_LOGGING_INFO); + } else if (!strcmp(configString, "debug")) { + capwap_logging_verboselevel(CAPWAP_LOGGING_DEBUG); + } else { + capwap_logging_error("Invalid configuration file, unknown logging.level value"); + return 0; + } + } + + /* Logging output interface */ + configSetting = config_lookup(config, "logging.output"); + if (configSetting != NULL) { + int count = config_setting_length(configSetting); + + /* Disable output interface */ + capwap_logging_disable_allinterface(); + + /* Enable selected interface */ + for (i = 0; i < count; i++) { + config_setting_t* configElement = config_setting_get_elem(configSetting, i); + if ((configElement != NULL) && (config_setting_lookup_string(configElement, "mode", &configString) == CONFIG_TRUE)) { + if (!strcmp(configString, "stdout")) { + capwap_logging_enable_console(0); + } else if (!strcmp(configString, "stderr")) { + capwap_logging_enable_console(1); + } else { + capwap_logging_error("Invalid configuration file, unknown logging.output value"); + return 0; + } + } + } + } + } + } + + /* Set name of AC */ + if (config_lookup_string(config, "application.name", &configString) == CONFIG_TRUE) { + if (strlen(configString) > CAPWAP_ACNAME_MAXLENGTH) { + capwap_logging_error("Invalid configuration file, application.name string length exceeded"); + return 0; + } + + strcpy(g_ac.acname.name, configString); + } + + /* Set binding of AC */ + configSetting = config_lookup(config, "application.binding"); + if (configSetting != NULL) { + int count = config_setting_length(configSetting); + + for (i = 0; i < count; i++) { + const char* bindingName = config_setting_get_string_elem(configSetting, i); + if (bindingName != NULL) { + unsigned short* binding = (unsigned short*)capwap_array_get_item_pointer(g_ac.binding, g_ac.binding->count); + + if (!strcmp(bindingName, "802.11")) { + *binding = CAPWAP_WIRELESS_BINDING_IEEE80211; + } else if (!strcmp(bindingName, "EPCGlobal")) { + *binding = CAPWAP_WIRELESS_BINDING_EPCGLOBAL; + } else { + capwap_logging_error("Invalid configuration file, unknown application.binding value"); + return 0; + } + } + } + } + + /* Set max stations of AC */ + if (config_lookup_int(config, "application.descriptor.maxstations", &configLongInt) == CONFIG_TRUE) { + if ((configLongInt > 0) && (configLongInt < 65536)) { + g_ac.descriptor.stationlimit = (unsigned short)configLongInt; + } else { + capwap_logging_error("Invalid configuration file, unknown application.descriptor.maxstations value"); + return 0; + } + } + + /* Set max wtp of AC */ + if (config_lookup_int(config, "application.descriptor.maxwtp", &configLongInt) == CONFIG_TRUE) { + if ((configLongInt > 0) && (configLongInt < 65536)) { + g_ac.descriptor.wtplimit = (unsigned short)configLongInt; + } else { + capwap_logging_error("Invalid configuration file, unknown application.descriptor.maxwtp value"); + return 0; + } + } + + /* Set security of AC */ + if (config_lookup(config, "application.descriptor.security") != NULL) { + g_ac.descriptor.security = 0; + if (config_lookup_bool(config, "application.descriptor.security.presharedkey", &configInt) == CONFIG_TRUE) { + if (configInt != 0) { + g_ac.descriptor.security |= CAPWAP_ACDESC_SECURITY_PRESHARED_KEY; + } + } + + if (config_lookup_bool(config, "application.descriptor.security.x509", &configInt) == CONFIG_TRUE) { + if (configInt != 0) { + g_ac.descriptor.security |= CAPWAP_ACDESC_SECURITY_X509_CERT; + } + } + } + + /* Set rmacfiled of AC */ + if (config_lookup_bool(config, "application.descriptor.rmacfiled.supported", &configInt) == CONFIG_TRUE) { + g_ac.descriptor.rmacfield = ((configInt != 0) ? CAPWAP_ACDESC_RMACFIELD_SUPPORTED : CAPWAP_ACDESC_RMACFIELD_NOTSUPPORTED); + } + + /* Set DTLS policy of AC */ + if (config_lookup(config, "application.descriptor.dtlspolicy") != NULL) { + g_ac.descriptor.dtlspolicy = 0; + if (config_lookup_bool(config, "application.descriptor.dtlspolicy.cleardatachannel", &configInt) == CONFIG_TRUE) { + if (configInt != 0) { + g_ac.descriptor.dtlspolicy |= CAPWAP_ACDESC_CLEAR_DATA_CHANNEL_ENABLED; + } + } + + if (config_lookup_bool(config, "application.descriptor.dtlspolicy.dtlsdatachannel", &configInt) == CONFIG_TRUE) { + if (configInt != 0) { + g_ac.descriptor.dtlspolicy |= CAPWAP_ACDESC_DTLS_DATA_CHANNEL_ENABLED; + } + } + } + + /* Set info descriptor of AC */ + configSetting = config_lookup(config, "application.descriptor.info"); + if (configSetting != NULL) { + int count = config_setting_length(configSetting); + + for (i = 0; i < count; i++) { + config_setting_t* configElement = config_setting_get_elem(configSetting, i); + if (configElement != NULL) { + long int configVendor; + if (config_setting_lookup_int(configElement, "idvendor", &configVendor) == CONFIG_TRUE) { + const char* configType; + if (config_setting_lookup_string(configElement, "type", &configType) == CONFIG_TRUE) { + const char* configValue; + if (config_setting_lookup_string(configElement, "value", &configValue) == CONFIG_TRUE) { + int lengthValue = strlen(configValue); + if (lengthValue < CAPWAP_ACDESC_SUBELEMENT_MAXDATA) { + unsigned short type; + struct capwap_acdescriptor_desc_subelement* desc; + + if (!strcmp(configType, "hardware")) { + type = CAPWAP_ACDESC_SUBELEMENT_HARDWAREVERSION; + } else if (!strcmp(configType, "software")) { + type = CAPWAP_ACDESC_SUBELEMENT_SOFTWAREVERSION; + } else { + capwap_logging_error("Invalid configuration file, unknown application.descriptor.info.type value"); + return 0; + } + + desc = (struct capwap_acdescriptor_desc_subelement*)capwap_array_get_item_pointer(g_ac.descriptor.descsubelement, g_ac.descriptor.descsubelement->count); + desc->vendor = (unsigned long)configVendor; + desc->type = type; + desc->length = lengthValue; + strcpy(desc->data, configValue); + } else { + capwap_logging_error("Invalid configuration file, application.descriptor.info.value string length exceeded"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, element application.descriptor.info.value not found"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, element application.descriptor.info.type not found"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, element application.descriptor.info.idvendor not found"); + return 0; + } + } + } + } + + /* Set ECN of AC */ + if (config_lookup_string(config, "application.ecn", &configString) == CONFIG_TRUE) { + if (!strcmp(configString, "full")) { + g_ac.dfa.ecn.flag = CAPWAP_FULL_ECN_SUPPORT; + } else if (!strcmp(configString, "limited")) { + g_ac.dfa.ecn.flag = CAPWAP_LIMITED_ECN_SUPPORT; + } else { + capwap_logging_error("Invalid configuration file, unknown application.ecn value"); + return 0; + } + } + + /* Set Timer of AC */ + if (config_lookup_int(config, "application.timer.discovery", &configLongInt) == CONFIG_TRUE) { + if ((configLongInt >= AC_DEFAULT_DISCOVERY_INTERVAL) && (configLongInt <= AC_MAX_DISCOVERY_INTERVAL)) { + g_ac.dfa.timers.discovery = (unsigned char)configLongInt; + } else { + capwap_logging_error("Invalid configuration file, invalid application.timer.discovery value"); + return 0; + } + } + + if (config_lookup_int(config, "application.timer.echorequest", &configLongInt) == CONFIG_TRUE) { + if ((configLongInt > 0) && (configLongInt < AC_MAX_ECHO_INTERVAL)) { + g_ac.dfa.timers.echorequest = (unsigned char)configLongInt; + } else { + capwap_logging_error("Invalid configuration file, invalid application.timer.echorequest value"); + return 0; + } + } + + if (config_lookup_int(config, "application.timer.decrypterrorreport", &configLongInt) == CONFIG_TRUE) { + if ((configLongInt > 0) && (configLongInt < 65536)) { + g_ac.dfa.decrypterrorreport_interval = (unsigned short)configLongInt; + } else { + capwap_logging_error("Invalid configuration file, invalid application.timer.decrypterrorreport value"); + return 0; + } + } + + if (config_lookup_int(config, "application.timer.idletimeout", &configLongInt) == CONFIG_TRUE) { + if (configLongInt > 0) { + g_ac.dfa.idletimeout.timeout = (unsigned long)configLongInt; + } else { + capwap_logging_error("Invalid configuration file, invalid application.timer.idletimeout value"); + return 0; + } + } + + /* Set wtpfallback of AC */ + if (config_lookup_bool(config, "application.wtpfallback", &configInt) == CONFIG_TRUE) { + g_ac.dfa.wtpfallback.mode = ((configInt != 0) ? CAPWAP_WTP_FALLBACK_ENABLED : CAPWAP_WTP_FALLBACK_DISABLED); + } + + /* Set DTLS of WTP */ + if (config_lookup_bool(config, "application.dtls.enable", &configInt) == CONFIG_TRUE) { + if (configInt != 0) { + struct capwap_dtls_param dtlsparam; + + /* Init dtls param */ + memset(&dtlsparam, 0, sizeof(struct capwap_dtls_param)); + dtlsparam.type = CAPWAP_DTLS_SERVER; + + /* Set DTLS type of AC */ + if (config_lookup_string(config, "application.dtls.type", &configString) == CONFIG_TRUE) { + if (!strcmp(configString, "x509")) { + dtlsparam.mode = CAPWAP_DTLS_MODE_CERTIFICATE; + } else if (!strcmp(configString, "presharedkey")) { + dtlsparam.mode = CAPWAP_DTLS_MODE_PRESHAREDKEY; + } else { + capwap_logging_error("Invalid configuration file, unknown application.dtls.type value"); + return 0; + } + } + + /* Set DTLS configuration of AC */ + if (dtlsparam.mode == CAPWAP_DTLS_MODE_CERTIFICATE) { + if (config_lookup_string(config, "application.dtls.x509.calist", &configString) == CONFIG_TRUE) { + if (strlen(configString) > 0) { + dtlsparam.cert.fileca = capwap_duplicate_string(configString); + } + } + + if (config_lookup_string(config, "application.dtls.x509.certificate", &configString) == CONFIG_TRUE) { + if (strlen(configString) > 0) { + dtlsparam.cert.filecert = capwap_duplicate_string(configString); + } + } + + if (config_lookup_string(config, "application.dtls.x509.privatekey", &configString) == CONFIG_TRUE) { + if (strlen(configString) > 0) { + dtlsparam.cert.filekey = capwap_duplicate_string(configString); + } + } + + if (config_lookup_string(config, "application.dtls.x509.privatekeypassword", &configString) == CONFIG_TRUE) { + if (strlen(configString) > 0) { + dtlsparam.cert.pwdprivatekey = capwap_duplicate_string(configString); + } + } + + if (dtlsparam.cert.fileca && dtlsparam.cert.filecert && dtlsparam.cert.filekey) { + if (capwap_crypt_createcontext(&g_ac.dtlscontext, &dtlsparam)) { + g_ac.enabledtls = 1; + } + } + + /* Free dtls param */ + if (dtlsparam.cert.fileca) { + capwap_free(dtlsparam.cert.fileca); + } + + if (dtlsparam.cert.filecert) { + capwap_free(dtlsparam.cert.filecert); + } + + if (dtlsparam.cert.filekey) { + capwap_free(dtlsparam.cert.filekey); + } + + if (dtlsparam.cert.pwdprivatekey) { + capwap_free(dtlsparam.cert.pwdprivatekey); + } + + if (!g_ac.enabledtls) { + return 0; + } + } else if (dtlsparam.mode == CAPWAP_DTLS_MODE_PRESHAREDKEY) { + /* TODO */ + } + } + } + + /* Set interface binding of AC */ + if (config_lookup_string(config, "application.network.binding", &configString) == CONFIG_TRUE) { + if (strlen(configString) > (IFNAMSIZ - 1)) { + capwap_logging_error("Invalid configuration file, application.network.binding string length exceeded"); + return 0; + } + + strcpy(g_ac.net.bind_interface, configString); + } + + /* Set mtu of AC */ + if (config_lookup_int(config, "application.network.mtu", &configLongInt) == CONFIG_TRUE) { + if ((configLongInt > 0) && (configLongInt < 65536)) { + g_ac.mtu = (unsigned short)configLongInt; + } else { + capwap_logging_error("Invalid configuration file, invalid application.network.mtu value"); + return 0; + } + } + + /* Set transport of AC */ + if (config_lookup_string(config, "application.network.transport", &configString) == CONFIG_TRUE) { + if (!strcmp(configString, "udp")) { + g_ac.dfa.transport.type = CAPWAP_UDP_TRANSPORT; + } else if (!strcmp(configString, "udplite")) { + g_ac.dfa.transport.type = CAPWAP_UDPLITE_TRANSPORT; + } else { + capwap_logging_error("Invalid configuration file, unknown application.network.transport value"); + return 0; + } + } + + /* Set ipv4 & ipv6 of AC */ + if (config_lookup_bool(config, "application.network.ipv4", &configIPv4) != CONFIG_TRUE) { + configIPv4 = 1; + } + + if (config_lookup_bool(config, "application.network.ipv6", &configIPv6) != CONFIG_TRUE) { + configIPv6 = 1; + } + + if (configIPv4 && configIPv6) { + g_ac.net.sock_family = AF_UNSPEC; + } else if (!configIPv4 && !configIPv6) { + capwap_logging_error("Invalid configuration file, request enable application.network.ipv4 or application.network.ipv6"); + return 0; + } else { + g_ac.net.sock_family = (configIPv4 ? AF_INET : AF_INET6); + } + + /* Set ip dual stack of WTP */ + if (config_lookup_bool(config, "application.network.ipdualstack", &configInt) == CONFIG_TRUE) { + if (!configInt) { + g_ac.net.bind_ctrl_flags |= CAPWAP_IPV6ONLY_FLAG; + g_ac.net.bind_data_flags |= CAPWAP_IPV6ONLY_FLAG; + } else { + g_ac.net.bind_ctrl_flags &= ~CAPWAP_IPV6ONLY_FLAG; + g_ac.net.bind_data_flags &= ~CAPWAP_IPV6ONLY_FLAG; + } + } + + return 1; +} + +/* Parsing configuration */ +static int ac_parsing_configuration(config_t* config) { + const char* configString; + + if (config_lookup_string(config, "version", &configString) == CONFIG_TRUE) { + if (strcmp(configString, "1.0") == 0) { + return ac_parsing_configuration_1_0(config); + } + + capwap_logging_error("Invalid configuration file, '%s' is not supported", configString); + } else { + capwap_logging_error("Invalid configuration file, unable to found version tag"); + } + + return 0; +} + + +/* Load configuration */ +static int ac_load_configuration(int argc, char** argv) { + int c; + int result = 0; + config_t config; + + ASSERT(argc >= 0); + ASSERT(argv != NULL); + + /* Parsing command line */ + opterr = 0; + while ((c = getopt(argc, argv, "hc:")) != -1) { + switch (c) { + case 'h': { + ac_print_usage(); + return 0; + } + + case 'c': { + if (strlen(optarg) < sizeof(g_configurationfile)) { + strcpy(g_configurationfile, optarg); + } else { + capwap_logging_error("Invalid -%c argument", optopt); + return -1; + } + + break; + } + + case '?': { + if (optopt == 'c') { + capwap_logging_error("Option -%c requires an argument", optopt); + } else { + capwap_logging_error("Unknown option character `\\x%x'", optopt); + } + + ac_print_usage(); + return -1; + } + } + } + + /* Init libconfig */ + config_init(&config); + + /* Load configuration */ + if (config_read_file(&config, g_configurationfile) == CONFIG_TRUE) { + result = ac_parsing_configuration(&config); + } else { + result = -1; + capwap_logging_error("Unable load the configuration file '%s': %s (%d)", g_configurationfile, config_error_text(&config), config_error_line(&config)); + } + + /* Free libconfig */ + config_destroy(&config); + return result; +} + +/* Init AC */ +static int ac_configure(void) { + /* Bind to any address */ + if (!capwap_bind_sockets(&g_ac.net)) { + capwap_logging_fatal("Cannot bind address"); + return AC_ERROR_NETWORK; + } + + return CAPWAP_SUCCESSFUL; +} + +/* Close AC */ +static void ac_close(void) { + ASSERT(g_ac.sessions->count == 0); + + /* Close socket */ + capwap_close_sockets(&g_ac.net); +} + +/* Check is valid binding */ +int ac_valid_binding(unsigned short binding) { + int i; + + for (i = 0; i < g_ac.binding->count; i++) { + if (binding == *(unsigned short*)capwap_array_get_item_pointer(g_ac.binding, i)) { + return 1; + } + } + + return 0; +} + +/* Main*/ +int main(int argc, char** argv) { + int value; + int result = CAPWAP_SUCCESSFUL; + + /* Init logging */ + capwap_logging_init(); + capwap_logging_verboselevel(CAPWAP_LOGGING_ERROR); + capwap_logging_enable_console(1); + + /* Init capwap */ + if (geteuid() != 0) { + capwap_logging_fatal("Request root privileges"); + return CAPWAP_REQUEST_ROOT; + } + + /* Init random generator */ + capwap_init_rand(); + + /* Init crypt */ + if (!capwap_crypt_init()) { + capwap_logging_fatal("Error to init crypt engine"); + return CAPWAP_CRYPT_ERROR; + } + + /* Alloc AC */ + if (!ac_init()) { + return AC_ERROR_SYSTEM_FAILER; + } + + /* Read configuration file */ + value = ac_load_configuration(argc, argv); + if (value < 0) { + result = AC_ERROR_LOAD_CONFIGURATION; + } else if (value > 0) { + /* Complete configuration AC */ + result = ac_configure(); + if (result == CAPWAP_SUCCESSFUL) { + /* Running AC */ + result = ac_execute(); + ac_close(); + } + } + + /* Free memory */ + ac_destroy(); + + /* Free crypt */ + capwap_crypt_free(); + + /* Check memory leak */ + if (capwap_check_memory_leak(1)) { + if (result == CAPWAP_SUCCESSFUL) + result = AC_ERROR_MEMORY_LEAK; + } + + /* Close logging */ + capwap_logging_close(); + + return result; +} diff --git a/src/ac/ac.h b/src/ac/ac.h new file mode 100644 index 0000000..3bdc460 --- /dev/null +++ b/src/ac/ac.h @@ -0,0 +1,117 @@ +#ifndef __CAPWAP_AC_HEADER__ +#define __CAPWAP_AC_HEADER__ + +/* standard include */ +#include "capwap.h" +#include "capwap_network.h" +#include "capwap_protocol.h" +#include "capwap_lock.h" +#include "capwap_list.h" +#include "capwap_event.h" +#include "capwap_element.h" + +#include + +/* AC Configuration */ +#define AC_DEFAULT_CONFIGURATION_FILE "/etc/capwap/ac.conf" + +#define AC_DEFAULT_MAXSTATION 128 +#define AC_DEFAULT_MAXSESSIONS 128 + +/* AC runtime error return code */ +#define AC_ERROR_SYSTEM_FAILER -1000 +#define AC_ERROR_LOAD_CONFIGURATION -1001 +#define AC_ERROR_NETWORK -1002 +#define AC_ERROR_MEMORY_LEAK 1 + +/* Min and max dfa values */ +#define AC_MIN_WAITDTLS_INTERVAL 30 +#define AC_DEFAULT_WAITDTLS_INTERVAL 60 +#define AC_MIN_WAITJOIN_INTERVAL 20 +#define AC_DEFAULT_WAITJOIN_INTERVAL 60 +#define AC_DEFAULT_CHANGE_STATE_PENDING_TIMER 25 +#define AC_MIN_DISCOVERY_INTERVAL 2 +#define AC_DEFAULT_DISCOVERY_INTERVAL 20 +#define AC_MAX_DISCOVERY_INTERVAL 180 +#define AC_DEFAULT_ECHO_INTERVAL 30 +#define AC_MAX_ECHO_INTERVAL 256 +#define AC_DEFAULT_DECRYPT_ERROR_PERIOD_INTERVAL 120 +#define AC_DEFAULT_IDLE_TIMEOUT_INTERVAL 300 +#define AC_DEFAULT_WTP_FALLBACK_MODE CAPWAP_WTP_FALLBACK_ENABLED +#define AC_DEFAULT_DATA_CHECK_TIMER 30 +#define AC_DEFAULT_RETRANSMIT_INTERVAL 3 +#define AC_MAX_RETRANSMIT 5 +#define AC_DEFAULT_DTLS_SESSION_DELETE 5 + +/* AC DFA */ +struct ac_state { + /* */ + struct capwap_ecnsupport_element ecn; + struct capwap_transport_element transport; + + struct capwap_timers_element timers; + unsigned short decrypterrorreport_interval; + struct capwap_idletimeout_element idletimeout; + struct capwap_wtpfallback_element wtpfallback; + + /* */ + capwap_acipv4list_element_array* acipv4list; + capwap_acipv6list_element_array* acipv6list; + + /* */ + int rfcWaitJoin; + int rfcChangeStatePendingTimer; + int rfcDataCheckTimer; + int rfcDTLSSessionDelete; + + /* Request retransmit */ + int rfcRetransmitInterval; + int rfcRetransmitCount; + int rfcMaxRetransmit; + + /* Dtls */ + int rfcWaitDTLS; +}; + +/* Handshake DTLS Data Channel */ +struct ac_data_session_handshake { + struct capwap_socket socket; + struct sockaddr_storage acaddress; + struct sockaddr_storage wtpaddress; + struct capwap_dtls dtls; +}; + +/* AC */ +struct ac_t { + int running; + + /* */ + struct ac_state dfa; + struct capwap_network net; + unsigned short mtu; + + struct capwap_array* binding; + + struct capwap_acname_element acname; + struct capwap_acdescriptor_element descriptor; + + /* Sessions */ + capwap_event_t changesessionlist; + struct capwap_list* sessions; + capwap_lock_t sessionslock; + struct capwap_list* datasessionshandshake; + + /* Dtls */ + int enabledtls; + struct capwap_dtls_context dtlscontext; +}; + +extern struct ac_t g_ac; + +/* Primary thread */ +int ac_execute(void); + +int ac_valid_binding(unsigned short binding); +void ac_update_statistics(void); + +#endif /* __CAPWAP_AC_HEADER__ */ diff --git a/src/ac/ac_dfa_configure.c b/src/ac/ac_dfa_configure.c new file mode 100644 index 0000000..8657db3 --- /dev/null +++ b/src/ac/ac_dfa_configure.c @@ -0,0 +1,128 @@ +#include "ac.h" +#include "capwap_dfa.h" +#include "capwap_array.h" +#include "ac_session.h" + +/* */ +int ac_dfa_state_configure(struct ac_session_t* session, struct capwap_packet* packet) { + unsigned long i; + int status = AC_DFA_ACCEPT_PACKET; + + ASSERT(session != NULL); + + if (packet) { + struct capwap_build_packet* buildpacket; + + buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket); + if (buildpacket) { + if (!capwap_build_packet_validate(buildpacket, NULL)) { + unsigned short binding; + + /* */ + binding = GET_WBID_HEADER(&buildpacket->header); + if (ac_valid_binding(binding) && (ntohl(buildpacket->ctrlmsg.type) == CAPWAP_CONFIGURATION_STATUS_REQUEST) && IS_SEQUENCE_SMALLER(session->remoteseqnumber, buildpacket->ctrlmsg.seq)) { + struct capwap_element_configurationstatus_request configurationstatusrequest; + + /* Configuration Status request info*/ + capwap_init_element_configurationstatus_request(&configurationstatusrequest, binding); + + /* Parsing elements list */ + if (capwap_parsing_element_configurationstatus_request(&configurationstatusrequest, buildpacket->elementslist->first)) { + struct capwap_build_packet* responsepacket; + + /* TODO: gestione richiesta */ + + /* Create response */ + responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, binding); + responsepacket->isctrlmsg = 1; + + /* Prepare configuration status response */ + capwap_build_packet_set_control_message_type(responsepacket, CAPWAP_CONFIGURATION_STATUS_RESPONSE, buildpacket->ctrlmsg.seq); + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_TIMERS_ELEMENT(&session->dfa.timers)); + + for (i = 0; i < configurationstatusrequest.radioadmstatus->count; i++) { + struct capwap_decrypterrorreportperiod_element report; + struct capwap_radioadmstate_element* radioadm = (struct capwap_radioadmstate_element*)capwap_array_get_item_pointer(configurationstatusrequest.radioadmstatus, i); + + report.radioid = radioadm->radioid; + report.interval = session->dfa.decrypterrorreport_interval; + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_DECRYPTERRORREPORTPERIOD_ELEMENT(&report)); + } + + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_IDLETIMEOUT_ELEMENT(&session->dfa.idletimeout)); + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_WTPFALLBACK_ELEMENT(&session->dfa.wtpfallback)); + + if (session->dfa.acipv4list->count > 0) { + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACIPV4LIST_ELEMENT(session->dfa.acipv4list)); + } + + if (session->dfa.acipv6list->count > 0) { + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACIPV6LIST_ELEMENT(session->dfa.acipv6list)); + } + + /* CAPWAP_CREATE_RADIOOPRSTATE_ELEMENT */ /* TODO */ + /* CAPWAP_CREATE_WTPSTATICIPADDRESS_ELEMENT */ /* TODO */ + /* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */ + + if (!capwap_build_packet_validate(responsepacket, NULL)) { + int result; + + /* Free old reference for this request */ + ac_free_reference_last_response(session); + + /* Send configuration status response to WTP */ + result = capwap_fragment_build_packet(responsepacket, session->responsefragmentpacket, session->mtu, session->fragmentid); + if (result >= 0) { + if (result == 1) { + session->fragmentid++; + } + + /* Save remote sequence number */ + session->remoteseqnumber = buildpacket->ctrlmsg.seq; + capwap_get_packet_digest((void*)packet->header, packet->packetsize, session->lastrecvpackethash); + + /* Send */ + for (i = 0; i < session->responsefragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->responsefragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) { + /* Response is already created and saved. When receive a re-request, DFA autoresponse */ + capwap_logging_debug("Warning: error to send configuration status response packet"); + break; + } + } + + /* Change status */ + ac_dfa_change_state(session, CAPWAP_DATA_CHECK_STATE); + capwap_set_timeout(session->dfa.rfcChangeStatePendingTimer, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } + } else { + capwap_logging_debug("Warning: build invalid configuration status response packet"); + } + + /* Free memory */ + capwap_build_packet_free(responsepacket); + } + + /* Free */ + capwap_free_element_configurationstatus_request(&configurationstatusrequest, binding); + } + } + + /* Free */ + capwap_build_packet_free(buildpacket); + } + } else { + /* Configure timeout */ + ac_dfa_change_state(session, CAPWAP_CONFIGURE_TO_DTLS_TEARDOWN_STATE); + status = AC_DFA_NO_PACKET; + } + + return status; +} + +/* */ +int ac_dfa_state_configure_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) { + return ac_session_teardown_connection(session); +} diff --git a/src/ac/ac_dfa_datacheck.c b/src/ac/ac_dfa_datacheck.c new file mode 100644 index 0000000..c42aeea --- /dev/null +++ b/src/ac/ac_dfa_datacheck.c @@ -0,0 +1,169 @@ +#include "ac.h" +#include "capwap_dfa.h" +#include "capwap_array.h" +#include "ac_session.h" + +/* */ +int ac_dfa_state_datacheck(struct ac_session_t* session, struct capwap_packet* packet) { + unsigned long i; + int status = AC_DFA_ACCEPT_PACKET; + + ASSERT(session != NULL); + + if (packet) { + struct capwap_build_packet* buildpacket; + + buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket); + if (buildpacket) { + if (!capwap_build_packet_validate(buildpacket, NULL)) { + unsigned short binding; + + /* */ + binding = GET_WBID_HEADER(&buildpacket->header); + if (ac_valid_binding(binding) && (ntohl(buildpacket->ctrlmsg.type) == CAPWAP_CHANGE_STATE_EVENT_REQUEST) && IS_SEQUENCE_SMALLER(session->remoteseqnumber, buildpacket->ctrlmsg.seq)) { + struct capwap_element_changestateevent_request changeeventrequest; + + /* Change event request info */ + capwap_init_element_changestateevent_request(&changeeventrequest, binding); + + /* Parsing elements list */ + if (capwap_parsing_element_changestateevent_request(&changeeventrequest, buildpacket->elementslist->first)) { + struct capwap_build_packet* responsepacket; + + /* TODO: gestione richiesta */ + + /* Create response */ + responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, binding); + responsepacket->isctrlmsg = 1; + + /* Prepare change event response */ + capwap_build_packet_set_control_message_type(responsepacket, CAPWAP_CHANGE_STATE_EVENT_RESPONSE, buildpacket->ctrlmsg.seq); + /* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */ + + if (!capwap_build_packet_validate(responsepacket, NULL)) { + int result; + + /* Free old reference for this request */ + ac_free_reference_last_response(session); + + /* Send change event response to WTP */ + result = capwap_fragment_build_packet(responsepacket, session->responsefragmentpacket, session->mtu, session->fragmentid); + if (result >= 0) { + if (result == 1) { + session->fragmentid++; + } + + /* Save remote sequence number */ + session->remoteseqnumber = buildpacket->ctrlmsg.seq; + capwap_get_packet_digest((void*)packet->header, packet->packetsize, session->lastrecvpackethash); + + /* Send */ + for (i = 0; i < session->responsefragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->responsefragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) { + /* Response is already created and saved. When receive a re-request, DFA autoresponse */ + capwap_logging_debug("Warning: error to send change event response packet"); + break; + } + } + + /* Change status */ + ac_dfa_change_state(session, CAPWAP_DATA_CHECK_TO_RUN_STATE); + capwap_set_timeout(session->dfa.rfcDataCheckTimer, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } + } else { + capwap_logging_debug("Warning: build invalid configuration status response packet"); + } + + /* Free memory */ + capwap_build_packet_free(responsepacket); + } + + /* Free */ + capwap_free_element_changestateevent_request(&changeeventrequest, binding); + } + } + + /* Free */ + capwap_build_packet_free(buildpacket); + } + } else { + /* Configure timeout */ + ac_dfa_change_state(session, CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE); + status = AC_DFA_NO_PACKET; + } + + return status; +} + +/* */ +int ac_dfa_state_datacheck_to_run(struct ac_session_t* session, struct capwap_packet* packet) { + int status = AC_DFA_ACCEPT_PACKET; + + ASSERT(session != NULL); + + if (packet) { + /* Wait Data Channel Keep-Alive packet */ + if (!packet->socket.isctrlsocket) { + struct capwap_build_packet* buildpacket; + + buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket); + if (buildpacket) { + struct capwap_sessionid_element sessionid; + + if (capwap_get_sessionid_from_keepalive(buildpacket, &sessionid)) { + if (!memcmp(&sessionid, &session->sessionid, sizeof(struct capwap_sessionid_element))) { + int result; + capwap_fragment_packet_array* txfragpacket; + + /* Receive data packet keepalive, response with same packet */ + txfragpacket = capwap_array_create(sizeof(struct capwap_packet), 0); + result = capwap_fragment_build_packet(buildpacket, txfragpacket, CAPWAP_DONT_FRAGMENT, 0); + if (!result) { + struct capwap_packet* txpacket; + + ASSERT(txfragpacket->count == 1); + + txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(txfragpacket, 0); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&session->datadtls, session->datasocket.socket[session->datasocket.type], txpacket->header, txpacket->packetsize, &session->acdataaddress, &session->wtpdataaddress)) { + capwap_logging_debug("Warning: error to send data channel keepalive packet"); + result = -1; + } + } + + /* */ + capwap_fragment_free(txfragpacket); + capwap_array_free(txfragpacket); + + if (!result) { + /* Capwap handshake complete */ + ac_dfa_change_state(session, CAPWAP_RUN_STATE); + capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } else { + ac_dfa_change_state(session, CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE); + status = AC_DFA_NO_PACKET; + } + } + } + + /* Free */ + capwap_build_packet_free(buildpacket); + } + } + } else { + /* Configure timeout */ + ac_dfa_change_state(session, CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE); + status = AC_DFA_NO_PACKET; + } + + return status; +} + +/* */ +int ac_dfa_state_datacheck_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) { + return ac_session_teardown_connection(session); +} diff --git a/src/ac/ac_dfa_dtls.c b/src/ac/ac_dfa_dtls.c new file mode 100644 index 0000000..f2a37c5 --- /dev/null +++ b/src/ac/ac_dfa_dtls.c @@ -0,0 +1,51 @@ +#include "ac.h" +#include "capwap_dfa.h" +#include "capwap_array.h" +#include "ac_session.h" + +/* DTLS BIO send */ +int ac_bio_send(struct capwap_dtls* dtls, char* buffer, int length, void* param) { + struct ac_session_t* session = (struct ac_session_t*)param; + struct capwap_socket* socket = ((dtls->session == CAPWAP_DTLS_CONTROL_SESSION) ? &session->ctrlsocket : &session->datasocket); + struct sockaddr_storage* wtpaddress = ((dtls->session == CAPWAP_DTLS_CONTROL_SESSION) ? &session->wtpctrladdress : &session->wtpdataaddress); + struct sockaddr_storage* acaddress = ((dtls->session == CAPWAP_DTLS_CONTROL_SESSION) ? &session->acctrladdress : &session->acdataaddress); + + return capwap_sendto(socket->socket[socket->type], buffer, length, acaddress, wtpaddress); +} + +/* */ +int ac_dfa_state_dtlssetup(struct ac_session_t* session, struct capwap_packet* packet) { + int status = AC_DFA_ACCEPT_PACKET; + + ASSERT(session != NULL); + ASSERT(packet == NULL); + + /* Create DTLS session */ + if (!capwap_crypt_createsession(&session->ctrldtls, CAPWAP_DTLS_CONTROL_SESSION, &g_ac.dtlscontext, ac_bio_send, session)) { + ac_dfa_change_state(session, CAPWAP_DTLS_SETUP_TO_IDLE_STATE); /* TODO */ + status = AC_DFA_NO_PACKET; + } else { + if (capwap_crypt_open(&session->ctrldtls, &session->wtpctrladdress) == CAPWAP_HANDSHAKE_ERROR) { + ac_dfa_change_state(session, CAPWAP_DTLS_SETUP_TO_IDLE_STATE); /* TODO */ + status = AC_DFA_NO_PACKET; + } else { + ac_dfa_change_state(session, CAPWAP_DTLS_CONNECT_STATE); + } + } + + return status; +} + +/* */ +int ac_dfa_state_dtlsconnect(struct ac_session_t* session, struct capwap_packet* packet) { + ASSERT(session != NULL); + ASSERT(packet == NULL); + + ac_dfa_change_state(session, CAPWAP_DTLS_CONNECT_TO_DTLS_TEARDOWN_STATE); /* TODO */ + return AC_DFA_NO_PACKET; +} + +/* */ +int ac_dfa_state_dtlsconnect_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) { + return ac_session_teardown_connection(session); +} diff --git a/src/ac/ac_dfa_imagedata.c b/src/ac/ac_dfa_imagedata.c new file mode 100644 index 0000000..afb7d5a --- /dev/null +++ b/src/ac/ac_dfa_imagedata.c @@ -0,0 +1,18 @@ +#include "ac.h" +#include "capwap_dfa.h" +#include "capwap_array.h" +#include "ac_session.h" + +/* */ +int ac_dfa_state_imagedata(struct ac_session_t* session, struct capwap_packet* packet) { + int status = AC_DFA_ACCEPT_PACKET; + + /* TODO */ + + return status; +} + +/* */ +int ac_dfa_state_imagedata_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) { + return ac_session_teardown_connection(session); +} diff --git a/src/ac/ac_dfa_join.c b/src/ac/ac_dfa_join.c new file mode 100644 index 0000000..99eaf57 --- /dev/null +++ b/src/ac/ac_dfa_join.c @@ -0,0 +1,272 @@ +#include "ac.h" +#include "capwap_dfa.h" +#include "capwap_array.h" +#include "ac_session.h" + +/* */ +int ac_dfa_state_join(struct ac_session_t* session, struct capwap_packet* packet) { + int i; + int status = AC_DFA_ACCEPT_PACKET; + struct capwap_resultcode_element resultcode = { CAPWAP_RESULTCODE_FAILURE }; + struct capwap_build_packet* responsepacket; + + ASSERT(session != NULL); + + if (packet) { + struct capwap_build_packet* buildpacket; + + buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket); + if (buildpacket) { + int validpacket; + unsigned long checkpacket; + struct capwap_array* returnedmessagearray = NULL; + capwap_unrecognized_element_array* unrecognizedarray; + struct capwap_element_join_request joinrequest; + unsigned short binding = GET_WBID_HEADER(&buildpacket->header); + + /* */ + unrecognizedarray = capwap_array_create(sizeof(struct unrecognized_info), 0); + + /* */ + checkpacket = capwap_build_packet_validate(buildpacket, unrecognizedarray); + if (!checkpacket) { + if (ac_valid_binding(binding)) { + if (ntohl(buildpacket->ctrlmsg.type) == CAPWAP_JOIN_REQUEST) { + resultcode.code = CAPWAP_RESULTCODE_SUCCESS; + } else { + resultcode.code = CAPWAP_RESULTCODE_MSG_UNEXPECTED_INVALID_CURRENT_STATE; + } + } else { + resultcode.code = CAPWAP_RESULTCODE_JOIN_FAILURE_BINDING_NOT_SUPPORTED; + } + } else { + if ((checkpacket & CAPWAP_MISSING_MANDATORY_MSG_ELEMENT) != 0) { + resultcode.code = CAPWAP_RESULTCODE_FAILURE_MISSING_MANDATORY_MSG_ELEMENT; + } else if ((checkpacket & CAPWAP_UNRECOGNIZED_MSG_ELEMENT) != 0) { + struct capwap_list_item* itemelement; + + resultcode.code = CAPWAP_RESULTCODE_FAILURE_UNRECOGNIZED_MESSAGE_ELEMENT; + returnedmessagearray = capwap_array_create(sizeof(struct capwap_returnedmessage_element), unrecognizedarray->count); + + for (i = 0; i < unrecognizedarray->count; i++) { + struct unrecognized_info* reasoninfo = capwap_array_get_item_pointer(unrecognizedarray, i); + + /* Search element */ + itemelement = buildpacket->elementslist->first; + while (itemelement != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)itemelement->item; + + if (ntohs(elementitem->type) == reasoninfo->element) { + struct capwap_returnedmessage_element* returnedelement = capwap_array_get_item_pointer(returnedmessagearray, i); + unsigned short length = sizeof(struct capwap_message_element) + ntohs(elementitem->length); + + returnedelement->reason = reasoninfo->reason; + returnedelement->length = min(length, CAPWAP_RETURNED_MESSAGE_MAX_LENGTH); + memcpy(&returnedelement->message[0], elementitem, returnedelement->length); + + break; + } + + /* Next */ + itemelement = itemelement->next; + } + } + } + } + + /* */ + capwap_array_free(unrecognizedarray); + + /* */ + capwap_init_element_join_request(&joinrequest, binding); + if (resultcode.code == CAPWAP_RESULTCODE_SUCCESS) { + /* Parsing elements list */ + if (capwap_parsing_element_join_request(&joinrequest, buildpacket->elementslist->first)) { + /* TODO: gestione richiesta */ + + /* Get sessionid */ + memcpy(&session->sessionid, joinrequest.sessionid, sizeof(struct capwap_sessionid_element)); + + /* Get binding */ + session->binding = binding; + + resultcode.code = CAPWAP_RESULTCODE_SUCCESS; + } + } + + /* Create response */ + responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, binding); + responsepacket->isctrlmsg = 1; + + /* Prepare join response */ + capwap_build_packet_set_control_message_type(responsepacket, CAPWAP_JOIN_RESPONSE, buildpacket->ctrlmsg.seq); + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_RESULTCODE_ELEMENT(&resultcode)); + + /* Check is valid packet after parsing request */ + validpacket = (((resultcode.code == CAPWAP_RESULTCODE_SUCCESS) || (resultcode.code == CAPWAP_RESULTCODE_SUCCESS_NAT_DETECTED)) ? 1 : 0); + if (validpacket) { + struct capwap_list* controllist; + struct capwap_list_item* item; + + /* Update statistics */ + ac_update_statistics(); + + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACDESCRIPTOR_ELEMENT(&g_ac.descriptor)); + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACNAME_ELEMENT(&g_ac.acname)); + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + for (i = 0; i < joinrequest.binding.ieee80211.wtpradioinformation->count; i++) { + struct capwap_80211_wtpradioinformation_element* radio; + + radio = (struct capwap_80211_wtpradioinformation_element*)capwap_array_get_item_pointer(joinrequest.binding.ieee80211.wtpradioinformation, i); + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_80211_WTPRADIOINFORMATION_ELEMENT(radio)); + } + } else { + capwap_logging_debug("Unknown capwap binding"); + } + + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ECNSUPPORT_ELEMENT(&session->dfa.ecn)); + + /* Get information from any local address */ + controllist = capwap_list_create(); + ac_get_control_information(controllist); + + for (item = controllist->first; item != NULL; item = item->next) { + struct ac_session_control* sessioncontrol = (struct ac_session_control*)item->item; + + if (sessioncontrol->localaddress.ss_family == AF_INET) { + struct capwap_controlipv4_element element; + + memcpy(&element.address, &((struct sockaddr_in*)&sessioncontrol->localaddress)->sin_addr, sizeof(struct in_addr)); + element.wtpcount = sessioncontrol->count; + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_CONTROLIPV4_ELEMENT(&element)); + } else if (sessioncontrol->localaddress.ss_family == AF_INET6) { + struct capwap_controlipv6_element element; + + memcpy(&element.address, &((struct sockaddr_in6*)&sessioncontrol->localaddress)->sin6_addr, sizeof(struct in6_addr)); + element.wtpcount = sessioncontrol->count; + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_CONTROLIPV6_ELEMENT(&element)); + } + } + + capwap_list_free(controllist); + + if (session->acctrladdress.ss_family == AF_INET) { + struct capwap_localipv4_element addr; + + memcpy(&addr.address, &((struct sockaddr_in*)&session->acctrladdress)->sin_addr, sizeof(struct in_addr)); + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_LOCALIPV4_ELEMENT(&addr)); + } else if (session->acctrladdress.ss_family == AF_INET6) { + struct capwap_localipv6_element addr; + + memcpy(&addr.address, &((struct sockaddr_in6*)&session->acctrladdress)->sin6_addr, sizeof(struct in6_addr)); + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_LOCALIPV6_ELEMENT(&addr)); + } + + /* CAPWAP_CREATE_ACIPV4LIST_ELEMENT */ /* TODO */ + /* CAPWAP_CREATE_ACIPV6LIST_ELEMENT */ /* TODO */ + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_TRANSPORT_ELEMENT(&session->dfa.transport)); + /* CAPWAP_CREATE_IMAGEIDENTIFIER_ELEMENT */ /* TODO */ + /* CAPWAP_CREATE_MAXIMUMMESSAGELENGTH_ELEMENT */ /* TODO */ + /* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */ + } else if (resultcode.code == CAPWAP_RESULTCODE_FAILURE_UNRECOGNIZED_MESSAGE_ELEMENT) { + ASSERT(returnedmessagearray != NULL); + + for (i = 0; i < returnedmessagearray->count; i++) { + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_RETURNEDMESSAGE_ELEMENT(capwap_array_get_item_pointer(returnedmessagearray, i))); + } + + capwap_array_free(returnedmessagearray); + } + + /* Validate packet */ + if (!validpacket || !capwap_build_packet_validate(responsepacket, NULL)) { + int result; + + /* Free old reference for this request */ + ac_free_reference_last_response(session); + + /* Send join response to WTP */ + result = capwap_fragment_build_packet(responsepacket, session->responsefragmentpacket, session->mtu, session->fragmentid); + if (result >= 0) { + if (result == 1) { + session->fragmentid++; + } + + /* Save remote sequence number */ + session->remoteseqnumber = buildpacket->ctrlmsg.seq; + capwap_get_packet_digest((void*)packet->header, packet->packetsize, session->lastrecvpackethash); + + /* Send */ + for (i = 0; i < session->responsefragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->responsefragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) { + /* Response is already created and saved. When receive a re-request, DFA autoresponse */ + capwap_logging_debug("Warning: error to send join response packet"); + break; + } + } + } + } else { + capwap_logging_debug("Warning: build invalid join response packet"); + } + + /* Free memory */ + capwap_build_packet_free(responsepacket); + capwap_free_element_join_request(&joinrequest, binding); + capwap_build_packet_free(buildpacket); + + /* Change state */ + if (validpacket) { + ac_dfa_change_state(session, CAPWAP_POSTJOIN_STATE); + } else { + ac_dfa_change_state(session, CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE); + status = AC_DFA_NO_PACKET; + } + } + } else { + /* Join timeout */ + ac_dfa_change_state(session, CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE); + status = AC_DFA_NO_PACKET; + } + + return status; +} + +/* */ +int ac_dfa_state_postjoin(struct ac_session_t* session, struct capwap_packet* packet) { + int status = AC_DFA_ACCEPT_PACKET; + + ASSERT(session != NULL); + + if (packet) { + unsigned short lengthpayload; + + lengthpayload = packet->packetsize - GET_HLEN_HEADER(packet->header) * 4; + if (lengthpayload >= sizeof(struct capwap_control_message)) { + struct capwap_control_message* ctrlmsg = (struct capwap_control_message*)packet->payload; + unsigned long type = ntohl(ctrlmsg->type); + + if (type == CAPWAP_CONFIGURATION_STATUS_REQUEST) { + ac_dfa_change_state(session, CAPWAP_CONFIGURE_STATE); + status = ac_dfa_state_configure(session, packet); + } else if (type == CAPWAP_IMAGE_DATA_REQUEST) { + ac_dfa_change_state(session, CAPWAP_IMAGE_DATA_STATE); + status = ac_dfa_state_imagedata(session, packet); + } + } + } else { + /* Join timeout */ + ac_dfa_change_state(session, CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE); + status = AC_DFA_NO_PACKET; + } + + return status; +} + +/* */ +int ac_dfa_state_join_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) { + return ac_session_teardown_connection(session); +} diff --git a/src/ac/ac_dfa_reset.c b/src/ac/ac_dfa_reset.c new file mode 100644 index 0000000..02b8976 --- /dev/null +++ b/src/ac/ac_dfa_reset.c @@ -0,0 +1,81 @@ +#include "ac.h" +#include "capwap_dfa.h" +#include "capwap_array.h" +#include "ac_session.h" + +/* */ +int ac_dfa_state_reset(struct ac_session_t* session, struct capwap_packet* packet) { + int status = AC_DFA_ACCEPT_PACKET; + + ASSERT(session != NULL); + + if (packet) { + if (!capwap_compare_ip(&session->wtpctrladdress, &packet->remoteaddr)) { + struct capwap_build_packet* buildpacket; + + /* Parsing packet */ + buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket); + if (buildpacket) { + if (!capwap_build_packet_validate(buildpacket, NULL)) { + unsigned short binding; + + /* */ + binding = GET_WBID_HEADER(&buildpacket->header); + if ((binding == session->binding) && (ntohl(buildpacket->ctrlmsg.type) == CAPWAP_RESET_RESPONSE) && ((session->localseqnumber - 1) == buildpacket->ctrlmsg.seq)) { + struct capwap_element_reset_response resetresponse; + + /* Valid packet, free request packet */ + ac_free_reference_last_request(session); + + /* Configuration status response info */ + capwap_init_element_reset_response(&resetresponse, binding); + + /* Parsing elements list */ + if (capwap_parsing_element_reset_response(&resetresponse, buildpacket->elementslist->first)) { + ac_dfa_change_state(session, CAPWAP_RESET_TO_DTLS_TEARDOWN_STATE); + status = AC_DFA_NO_PACKET; + } + + /* Free join response */ + capwap_free_element_reset_response(&resetresponse, binding); + } + } + + /* Free */ + capwap_build_packet_free(buildpacket); + } + } + } else { + int i; + + /* No Configuration status response received */ + session->dfa.rfcRetransmitCount++; + if (session->dfa.rfcRetransmitCount >= session->dfa.rfcMaxRetransmit) { + /* Timeout join state */ + ac_free_reference_last_request(session); + ac_dfa_change_state(session, CAPWAP_RESET_TO_DTLS_TEARDOWN_STATE); + status = AC_DFA_NO_PACKET; + } else { + /* Retransmit configuration request */ + for (i = 0; i < session->requestfragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->requestfragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) { + capwap_logging_debug("Warning: error to send configuration status request packet"); + break; + } + } + + /* Update timeout */ + capwap_set_timeout(session->dfa.rfcRetransmitInterval, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } + } + + return status; +} + +/* */ +int ac_dfa_state_reset_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) { + return ac_session_teardown_connection(session); +} diff --git a/src/ac/ac_dfa_run.c b/src/ac/ac_dfa_run.c new file mode 100644 index 0000000..1ebea6b --- /dev/null +++ b/src/ac/ac_dfa_run.c @@ -0,0 +1,263 @@ +#include "ac.h" +#include "capwap_dfa.h" +#include "capwap_array.h" +#include "ac_session.h" + +/* */ +static int receive_echo_request(struct ac_session_t* session, struct capwap_build_packet* buildpacket, struct capwap_packet* packet) { + unsigned long i; + unsigned short binding; + + ASSERT(session != NULL); + ASSERT(buildpacket != NULL); + ASSERT(packet != NULL); + + /* */ + binding = GET_WBID_HEADER(&buildpacket->header); + if (ac_valid_binding(binding) && IS_SEQUENCE_SMALLER(session->remoteseqnumber, buildpacket->ctrlmsg.seq)) { + struct capwap_element_echo_request echorequest; + + /* Echo request info*/ + capwap_init_element_echo_request(&echorequest, binding); + + /* Parsing elements list */ + if (capwap_parsing_element_echo_request(&echorequest, buildpacket->elementslist->first)) { + struct capwap_build_packet* responsepacket; + + /* Create response */ + responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, binding); + responsepacket->isctrlmsg = 1; + + /* Prepare echo response */ + capwap_build_packet_set_control_message_type(responsepacket, CAPWAP_ECHO_RESPONSE, buildpacket->ctrlmsg.seq); + /* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */ + + if (!capwap_build_packet_validate(responsepacket, NULL)) { + int result; + + /* Free old reference for this request */ + ac_free_reference_last_response(session); + + /* Send echo response to WTP */ + result = capwap_fragment_build_packet(responsepacket, session->responsefragmentpacket, session->mtu, session->fragmentid); + if (result >= 0) { + if (result == 1) { + session->fragmentid++; + } + + /* Save remote sequence number */ + session->remoteseqnumber = buildpacket->ctrlmsg.seq; + capwap_get_packet_digest((void*)packet->header, packet->packetsize, session->lastrecvpackethash); + + /* Send */ + for (i = 0; i < session->responsefragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->responsefragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) { + /* Response is already created and saved. When receive a re-request, DFA autoresponse */ + capwap_logging_debug("Warning: error to send echo response packet"); + break; + } + } + } + } + + /* Free memory */ + capwap_build_packet_free(responsepacket); + } + + /* Free */ + capwap_free_element_echo_request(&echorequest, binding); + } + + return 0; +} + +/* */ +int ac_dfa_state_run(struct ac_session_t* session, struct capwap_packet* packet) { + int status = AC_DFA_ACCEPT_PACKET; + + ASSERT(session != NULL); + + if (packet) { + struct capwap_build_packet* buildpacket; + + buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket); + if (buildpacket) { + if (!capwap_build_packet_validate(buildpacket, NULL)) { + if (packet->socket.isctrlsocket) { + unsigned long typemsg = ntohl(buildpacket->ctrlmsg.type); + + if (capwap_is_request_type(typemsg) || ((session->localseqnumber - 1) == buildpacket->ctrlmsg.seq)) { + switch (typemsg) { + case CAPWAP_CONFIGURATION_UPDATE_REQUEST: { + /* TODO */ + capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + break; + } + + case CAPWAP_CHANGE_STATE_EVENT_RESPONSE: { + /* TODO */ + capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + break; + } + + case CAPWAP_ECHO_REQUEST: { + if (!receive_echo_request(session, buildpacket, packet)) { + capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } else { + ac_dfa_change_state(session, CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE); + status = AC_DFA_NO_PACKET; + } + + break; + } + + case CAPWAP_CLEAR_CONFIGURATION_REQUEST: { + /* TODO */ + capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + break; + } + + case CAPWAP_WTP_EVENT_RESPONSE: { + /* TODO */ + capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + break; + } + + case CAPWAP_DATA_TRANSFER_REQUEST: { + /* TODO */ + capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + break; + } + + case CAPWAP_DATA_TRANSFER_RESPONSE: { + /* TODO */ + capwap_set_timeout(AC_MAX_ECHO_INTERVAL, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + break; + } + } + } + } else { + if (IS_FLAG_K_HEADER(&buildpacket->header)) { + struct capwap_sessionid_element sessionid; + + if (capwap_get_sessionid_from_keepalive(buildpacket, &sessionid)) { + if (!memcmp(&sessionid, &session->sessionid, sizeof(struct capwap_sessionid_element))) { + int result; + capwap_fragment_packet_array* txfragpacket; + + /* Receive data packet keepalive, response with same packet */ + txfragpacket = capwap_array_create(sizeof(struct capwap_packet), 0); + result = capwap_fragment_build_packet(buildpacket, txfragpacket, CAPWAP_DONT_FRAGMENT, 0); + if (!result) { + struct capwap_packet* txpacket; + + ASSERT(txfragpacket->count == 1); + + txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(txfragpacket, 0); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&session->datadtls, session->datasocket.socket[session->datasocket.type], txpacket->header, txpacket->packetsize, &session->acdataaddress, &session->wtpdataaddress)) { + capwap_logging_debug("Warning: error to send data channel keepalive packet"); + result = -1; + } + } + + /* */ + capwap_fragment_free(txfragpacket); + capwap_array_free(txfragpacket); + + if (result) { + ac_dfa_change_state(session, CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE); + status = AC_DFA_NO_PACKET; + } + } + } + } else { + /* TODO */ + } + } + } + + /* Free */ + capwap_build_packet_free(buildpacket); + } + } else { + ac_dfa_change_state(session, CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE); + status = AC_DFA_NO_PACKET; + } + + return status; +} + +/* */ +int ac_dfa_state_run_to_reset(struct ac_session_t* session, struct capwap_packet* packet) { + int status = AC_DFA_NO_PACKET; + struct capwap_build_packet* buildpacket; + + ASSERT(session != NULL); + ASSERT(packet == NULL); + + /* Build packet */ + buildpacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, session->binding); + buildpacket->isctrlmsg = 1; + + /* Prepare reset request */ + capwap_build_packet_set_control_message_type(buildpacket, CAPWAP_RESET_REQUEST, session->localseqnumber++); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_IMAGEIDENTIFIER_ELEMENT(&session->startupimage)); + /* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */ + + if (!capwap_build_packet_validate(buildpacket, NULL)) { + int i; + int result; + + /* Free old reference for this request */ + ac_free_reference_last_request(session); + + /* Send reset request to WTP */ + result = capwap_fragment_build_packet(buildpacket, session->requestfragmentpacket, session->mtu, session->fragmentid); + if (result >= 0) { + if (result == 1) { + session->fragmentid++; + } + + /* Send */ + for (i = 0; i < session->requestfragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(session->requestfragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&session->ctrldtls, session->ctrlsocket.socket[session->ctrlsocket.type], txpacket->header, txpacket->packetsize, &session->acctrladdress, &session->wtpctrladdress)) { + capwap_logging_debug("Warning: error to send reset request packet"); + result = -1; + break; + } + } + } + + if (result == -1) { + /* Error to send packets */ + ac_free_reference_last_request(session); + ac_dfa_change_state(session, CAPWAP_RESET_TO_DTLS_TEARDOWN_STATE); + } else { + session->dfa.rfcRetransmitCount = 0; + + capwap_killall_timeout(&session->timeout); + capwap_set_timeout(session->dfa.rfcRetransmitInterval, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + + ac_dfa_change_state(session, CAPWAP_RESET_STATE); + status = AC_DFA_ACCEPT_PACKET; + } + } + + /* Free memory */ + capwap_build_packet_free(buildpacket); + + return status; +} + +/* */ +int ac_dfa_state_run_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet) { + return ac_session_teardown_connection(session); +} diff --git a/src/ac/ac_dfa_teardown.c b/src/ac/ac_dfa_teardown.c new file mode 100644 index 0000000..1fc5377 --- /dev/null +++ b/src/ac/ac_dfa_teardown.c @@ -0,0 +1,22 @@ +#include "ac.h" +#include "capwap_dfa.h" +#include "capwap_array.h" +#include "ac_session.h" + +/* */ +int ac_dfa_state_teardown(struct ac_session_t* session, struct capwap_packet* packet) { + ASSERT(session != NULL); + ASSERT(packet == NULL); + + /* Defered free resource */ + ac_dfa_change_state(session, CAPWAP_DEAD_STATE); + return AC_DFA_DROP_PACKET; +} + +/* */ +int ac_dfa_state_dead(struct ac_session_t* session, struct capwap_packet* packet) { + ASSERT(session != NULL); + ASSERT(packet == NULL); + + return AC_DFA_DEAD; +} diff --git a/src/ac/ac_discovery.c b/src/ac/ac_discovery.c new file mode 100644 index 0000000..20c04cc --- /dev/null +++ b/src/ac/ac_discovery.c @@ -0,0 +1,276 @@ +#include "ac.h" +#include "capwap_protocol.h" +#include "ac_discovery.h" +#include "ac_session.h" + +#define AC_DISCOVERY_CLEANUP_TIMEOUT 1000 + +struct ac_discovery_t { + pthread_t threadid; + int endthread; + + unsigned short fragmentid; + unsigned char txseqnumber; + + capwap_event_t waitpacket; + capwap_lock_t packetslock; + struct capwap_list* packets; +}; + +struct ac_discovery_packet { + int sendsock; + struct sockaddr_storage sender; + char data[0]; +}; + +static struct ac_discovery_t g_ac_discovery; + +/* */ +int ac_discovery_start(void) { + int result; + + memset(&g_ac_discovery, 0, sizeof(struct ac_discovery_t)); + + /* Init */ + capwap_event_init(&g_ac_discovery.waitpacket); + capwap_lock_init(&g_ac_discovery.packetslock); + g_ac_discovery.packets = capwap_list_create(); + + /* Create thread */ + result = pthread_create(&g_ac_discovery.threadid, NULL, ac_discovery_thread, NULL); + if (result) { + capwap_logging_debug("Unable create discovery thread"); + return 0; + } + + return 1; +} + +/* */ +void ac_discovery_stop(void) { + void* dummy; + + g_ac_discovery.endthread = 1; + capwap_event_signal(&g_ac_discovery.waitpacket); + pthread_join(g_ac_discovery.threadid, &dummy); + + /* Free memory */ + capwap_event_destroy(&g_ac_discovery.waitpacket); + capwap_lock_exit(&g_ac_discovery.packetslock); + capwap_list_free(g_ac_discovery.packets); +} + +/* */ +void ac_discovery_add_packet(void* buffer, int buffersize, int sock, struct sockaddr_storage* sender) { + struct capwap_list_item* item; + struct ac_discovery_packet* packet; + + ASSERT(buffer != NULL); + ASSERT(buffersize > 0); + ASSERT(sock >= 0); + ASSERT(sender != NULL); + + /* TODO: mettere un history delle discovery request già processate per non eseguirle di nuovo */ + /* L'elemento deve rimanere per la durata minima di una discovery request */ + + /* Copy packet */ + item = capwap_itemlist_create(sizeof(struct ac_discovery_packet) + buffersize); + packet = (struct ac_discovery_packet*)item->item; + packet->sendsock = sock; + memcpy(&packet->sender, sender, sizeof(struct sockaddr_storage)); + memcpy(packet->data, buffer, buffersize); + + /* Append to packets list */ + capwap_lock_enter(&g_ac_discovery.packetslock); + capwap_itemlist_insert_after(g_ac_discovery.packets, NULL, item); + capwap_event_signal(&g_ac_discovery.waitpacket); + capwap_lock_exit(&g_ac_discovery.packetslock); +} + +/* */ +static struct capwap_build_packet* ac_create_discovery_response(struct capwap_build_packet* packet, struct capwap_element_discovery_request* discoveryrequest, struct sockaddr_storage* sender) { + int i; + unsigned short binding; + struct capwap_list* controllist; + struct capwap_list_item* item; + struct capwap_build_packet* responsepacket; + + ASSERT(packet != NULL); + ASSERT(discoveryrequest != NULL); + ASSERT(sender != NULL); + + /* Check is valid binding */ + binding = GET_WBID_HEADER(&packet->header); + if (!ac_valid_binding(binding)) { + return NULL; + } + + /* Build packet */ + responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, binding); + responsepacket->isctrlmsg = 1; + + /* Update statistics */ + ac_update_statistics(); + + /* Prepare discovery response */ + capwap_build_packet_set_control_message_type(responsepacket, CAPWAP_DISCOVERY_RESPONSE, packet->ctrlmsg.seq); + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACDESCRIPTOR_ELEMENT(&g_ac.descriptor)); + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_ACNAME_ELEMENT(&g_ac.acname)); + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + for (i = 0; i < discoveryrequest->binding.ieee80211.wtpradioinformation->count; i++) { + struct capwap_80211_wtpradioinformation_element* radio; + + radio = (struct capwap_80211_wtpradioinformation_element*)capwap_array_get_item_pointer(discoveryrequest->binding.ieee80211.wtpradioinformation, i); + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_80211_WTPRADIOINFORMATION_ELEMENT(radio)); + } + } else { + capwap_logging_debug("Unknown capwap binding"); + } + + /* Get information from any local address */ + controllist = capwap_list_create(); + ac_get_control_information(controllist); + + for (item = controllist->first; item != NULL; item = item->next) { + struct ac_session_control* sessioncontrol = (struct ac_session_control*)item->item; + + if (sessioncontrol->localaddress.ss_family == AF_INET) { + struct capwap_controlipv4_element element; + + memcpy(&element.address, &((struct sockaddr_in*)&sessioncontrol->localaddress)->sin_addr, sizeof(struct in_addr)); + element.wtpcount = sessioncontrol->count; + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_CONTROLIPV4_ELEMENT(&element)); + } else if (sessioncontrol->localaddress.ss_family == AF_INET6) { + struct capwap_controlipv6_element element; + + memcpy(&element.address, &((struct sockaddr_in6*)&sessioncontrol->localaddress)->sin6_addr, sizeof(struct in6_addr)); + element.wtpcount = sessioncontrol->count; + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_CONTROLIPV6_ELEMENT(&element)); + } + } + + capwap_list_free(controllist); + + /* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */ + + return responsepacket; +} + +/* Cleanup info discovery */ +static void ac_discovery_cleanup(void) { + /* Clean history discovery request */ + /* TODO */ +} + +/* */ +static void ac_discovery_run(void) { + int sizedata; + struct capwap_list_item* itempacket; + struct capwap_build_packet* buildpacket; + struct ac_discovery_packet* packet; + unsigned short binding; + + while (!g_ac_discovery.endthread) { + /* Get packet */ + capwap_lock_enter(&g_ac_discovery.packetslock); + + itempacket = NULL; + if (g_ac_discovery.packets->count > 0) { + itempacket = capwap_itemlist_remove_head(g_ac_discovery.packets); + } + + capwap_lock_exit(&g_ac_discovery.packetslock); + + if (!itempacket) { + /* Wait packet with timeout*/ + if (!capwap_event_wait_timeout(&g_ac_discovery.waitpacket, AC_DISCOVERY_CLEANUP_TIMEOUT)) { + ac_discovery_cleanup(); + } + + continue; + } + + /* */ + packet = (struct ac_discovery_packet*)itempacket->item; + sizedata = itempacket->itemsize - sizeof(struct ac_discovery_packet); + + /* Parsing packet */ + buildpacket = capwap_rx_packet_create(packet->data, sizedata, 1); + if (buildpacket) { + if (!capwap_build_packet_validate(buildpacket, NULL)) { + struct capwap_element_discovery_request discoveryrequest; + + /* */ + binding = GET_WBID_HEADER(&buildpacket->header); + capwap_init_element_discovery_request(&discoveryrequest, binding); + + /* Parsing elements list */ + if (capwap_parsing_element_discovery_request(&discoveryrequest, buildpacket->elementslist->first)) { + struct capwap_build_packet* txpacket; + capwap_fragment_packet_array* responsefragmentpacket = NULL; + + /* Creare discovery response */ + txpacket = ac_create_discovery_response(buildpacket, &discoveryrequest, &packet->sender); + if (txpacket) { + int result = -1; + + if (!capwap_build_packet_validate(txpacket, NULL)) { + responsefragmentpacket = capwap_array_create(sizeof(struct capwap_packet), 0); + result = capwap_fragment_build_packet(txpacket, responsefragmentpacket, g_ac.mtu, g_ac_discovery.fragmentid); + if (result == 1) { + g_ac_discovery.fragmentid++; + } + } else { + capwap_logging_debug("Warning: build invalid discovery response packet"); + } + + capwap_build_packet_free(txpacket); + + /* Send discovery response to WTP */ + if (result >= 0) { + int i; + + for (i = 0; i < responsefragmentpacket->count; i++) { + struct capwap_packet* sendpacket = (struct capwap_packet*)capwap_array_get_item_pointer(responsefragmentpacket, i); + ASSERT(sendpacket != NULL); + + if (!capwap_sendto(packet->sendsock, sendpacket->header, sendpacket->packetsize, NULL, &packet->sender)) { + capwap_logging_debug("Warning: error to send discovery response packet"); + break; + } + } + } + } + + /* Don't buffering a packets sent */ + if (responsefragmentpacket) { + capwap_fragment_free(responsefragmentpacket); + capwap_array_free(responsefragmentpacket); + } + } + + /* Free discovery request */ + capwap_free_element_discovery_request(&discoveryrequest, binding); + } + + /* */ + capwap_build_packet_free(buildpacket); + } + + /* Free packet */ + capwap_itemlist_free(itempacket); + } +} + +/* */ +void* ac_discovery_thread(void* param) { + + capwap_logging_debug("Discovery start"); + ac_discovery_run(); + capwap_logging_debug("Discovery stop"); + + /* Thread exit */ + pthread_exit(NULL); + return NULL; +} diff --git a/src/ac/ac_discovery.h b/src/ac/ac_discovery.h new file mode 100644 index 0000000..08bb110 --- /dev/null +++ b/src/ac/ac_discovery.h @@ -0,0 +1,10 @@ +#ifndef __AC_DISCOVERY_HEADER__ +#define __AC_DISCOVERY_HEADER__ + +void* ac_discovery_thread(void* param); + +int ac_discovery_start(void); +void ac_discovery_stop(void); +void ac_discovery_add_packet(void* buffer, int buffersize, int sock, struct sockaddr_storage* sender); + +#endif /* __AC_DISCOVERY_HEADER__ */ diff --git a/src/ac/ac_execute.c b/src/ac/ac_execute.c new file mode 100644 index 0000000..5bd4e48 --- /dev/null +++ b/src/ac/ac_execute.c @@ -0,0 +1,532 @@ +#include "ac.h" +#include "capwap_dfa.h" +#include "capwap_event.h" +#include "ac_session.h" +#include "ac_discovery.h" + +#include + +/* Add packet to session */ +static void ac_session_add_packet(struct ac_session_t* session, char* buffer, int size, int isctrlsocket, int plainbuffer) { + struct capwap_list_item* item; + struct ac_packet* packet; + + ASSERT(session != NULL); + ASSERT(buffer != NULL); + ASSERT(size > 0); + + /* Copy packet */ + item = capwap_itemlist_create(sizeof(struct ac_packet) + size); + packet = (struct ac_packet*)item->item; + packet->plainbuffer = plainbuffer; + memcpy(packet->buffer, buffer, size); + + /* Append to packets list */ + capwap_lock_enter(&session->packetslock); + capwap_itemlist_insert_after((isctrlsocket ? session->controlpackets : session->datapackets), NULL, item); + capwap_event_signal(&session->waitpacket); + capwap_lock_exit(&session->packetslock); +} + +/* Find AC sessions */ +static struct ac_session_t* ac_search_session_from_wtpaddress(struct sockaddr_storage* address, int isctrlsocket) { + struct ac_session_t* result = NULL; + struct capwap_list_item* search; + + ASSERT(address != NULL); + + capwap_lock_enter(&g_ac.sessionslock); + + search = g_ac.sessions->first; + while (search != NULL) { + struct ac_session_t* session = (struct ac_session_t*)search->item; + ASSERT(session != NULL); + + if (!capwap_compare_ip(address, (isctrlsocket ? &session->wtpctrladdress : &session->wtpdataaddress))) { + session->count++; + result = session; + break; + } + + search = search->next; + } + + capwap_lock_exit(&g_ac.sessionslock); + + return result; +} + +/* */ +static struct ac_session_t* ac_get_session_from_keepalive(void* buffer, int buffersize) { + struct ac_session_t* result = NULL; + struct capwap_build_packet* buildpacket; + + ASSERT(buffer != NULL); + ASSERT(buffersize > 0); + + buildpacket = capwap_rx_packet_create(buffer, buffersize, 0); + if (buildpacket) { + struct capwap_sessionid_element sessionid; + + if (capwap_get_sessionid_from_keepalive(buildpacket, &sessionid)) { + struct capwap_list_item* search; + + capwap_lock_enter(&g_ac.sessionslock); + + search = g_ac.sessions->first; + while (search != NULL) { + struct ac_session_t* session = (struct ac_session_t*)search->item; + ASSERT(session != NULL); + + if (!memcmp(&sessionid, &session->sessionid, sizeof(struct capwap_sessionid_element))) { + session->count++; + result = session; + break; + } + + search = search->next; + } + + capwap_lock_exit(&g_ac.sessionslock); + } + + /* Free */ + capwap_build_packet_free(buildpacket); + } + + return result; + +} + +/* Close session */ +static void ac_close_session(struct ac_session_t* session) { + session->closesession = 1; + capwap_event_signal(&session->waitpacket); +} + +/* Close sessions */ +static void ac_close_sessions() { + struct capwap_list_item* search; + + capwap_lock_enter(&g_ac.sessionslock); + + search = g_ac.sessions->first; + while (search != NULL) { + struct ac_session_t* session = (struct ac_session_t*)search->item; + ASSERT(session != NULL); + + ac_close_session(session); + + search = search->next; + } + + capwap_lock_exit(&g_ac.sessionslock); +} + +/* DTLS Handshake BIO send */ +static int ac_bio_handshake_send(struct capwap_dtls* dtls, char* buffer, int length, void* param) { + struct ac_data_session_handshake* handshake = (struct ac_data_session_handshake*)param; + return capwap_sendto(handshake->socket.socket[handshake->socket.type], buffer, length, &handshake->acaddress, &handshake->wtpaddress); +} + +/* Find AC sessions */ +static void ac_update_session_from_datapacket(struct capwap_socket* socket, struct sockaddr_storage* recvfromaddr, struct sockaddr_storage* recvtoaddr, void* buffer, int buffersize) { + struct ac_session_t* session = NULL; + struct capwap_preamble* preamble = (struct capwap_preamble*)buffer; + + ASSERT(buffer != NULL); + ASSERT(buffersize > sizeof(struct capwap_preamble)); + ASSERT(socket != NULL); + ASSERT(recvfromaddr != NULL); + ASSERT(recvtoaddr != NULL); + + /* */ + if (preamble->type == CAPWAP_PREAMBLE_HEADER) { + if ((g_ac.descriptor.dtlspolicy & CAPWAP_ACDESC_CLEAR_DATA_CHANNEL_ENABLED) != 0) { + session = ac_get_session_from_keepalive(buffer, buffersize); + if (session) { + /* Update data session */ + memcpy(&session->datasocket, socket, sizeof(struct capwap_socket)); + memcpy(&session->acdataaddress, recvtoaddr, sizeof(struct sockaddr_storage)); + memcpy(&session->wtpdataaddress, recvfromaddr, sizeof(struct sockaddr_storage)); + + /* Add packet*/ + ac_session_add_packet(session, buffer, buffersize, 0, 1); + ac_session_release_reference(session); + } + } + } else if (preamble->type == CAPWAP_PREAMBLE_DTLS_HEADER) { + if ((g_ac.descriptor.dtlspolicy & CAPWAP_ACDESC_DTLS_DATA_CHANNEL_ENABLED) != 0) { + struct capwap_list_item* itemlist; + struct ac_data_session_handshake* handshake; + + /* Search active data dtls handshake */ + itemlist = g_ac.datasessionshandshake->first; + while (itemlist != NULL) { + handshake = (struct ac_data_session_handshake*)itemlist->item; + + if (!capwap_compare_ip(recvfromaddr, &handshake->wtpaddress) && !capwap_compare_ip(recvtoaddr, &handshake->acaddress)) { + break; + } + + /* Next */ + itemlist = itemlist->next; + } + + /* Create new DTLS handshake */ + if (!itemlist) { + itemlist = capwap_itemlist_create(sizeof(struct ac_data_session_handshake)); + handshake = (struct ac_data_session_handshake*)itemlist->item; + memset(handshake, 0, sizeof(struct ac_data_session_handshake)); + + /* */ + memcpy(&handshake->socket, socket, sizeof(struct capwap_socket)); + memcpy(&handshake->acaddress, recvtoaddr, sizeof(struct sockaddr_storage)); + memcpy(&handshake->wtpaddress, recvfromaddr, sizeof(struct sockaddr_storage)); + + /* Create DTLS session */ + if (!capwap_crypt_createsession(&handshake->dtls, CAPWAP_DTLS_DATA_SESSION, &g_ac.dtlscontext, ac_bio_handshake_send, handshake)) { + capwap_itemlist_free(itemlist); + itemlist = NULL; + } else { + if (capwap_crypt_open(&handshake->dtls, recvfromaddr) == CAPWAP_HANDSHAKE_ERROR) { + capwap_crypt_freesession(&handshake->dtls); + capwap_itemlist_free(itemlist); + itemlist = NULL; + } else { + /* Add item to list */ + capwap_itemlist_insert_after(g_ac.datasessionshandshake, NULL, itemlist); + } + } + } + + /* Decrypt packet */ + if (itemlist) { + char temp[CAPWAP_MAX_PACKET_SIZE]; + + /* */ + handshake = (struct ac_data_session_handshake*)itemlist->item; + buffersize = capwap_decrypt_packet(&handshake->dtls, buffer, buffersize, temp, CAPWAP_MAX_PACKET_SIZE); + if (buffersize > 0) { + session = ac_get_session_from_keepalive(temp, buffersize); + if (!session) { + capwap_itemlist_remove(g_ac.datasessionshandshake, itemlist); + capwap_crypt_close(&handshake->dtls); + capwap_crypt_freesession(&handshake->dtls); + capwap_itemlist_free(itemlist); + } else { + /* Update DTLS session */ + capwap_crypt_change_dtls(&handshake->dtls, &session->datadtls); + memcpy(&session->datasocket, &handshake->socket, sizeof(struct capwap_socket)); + memcpy(&session->acdataaddress, &handshake->acaddress, sizeof(struct sockaddr_storage)); + memcpy(&session->wtpdataaddress, &handshake->wtpaddress, sizeof(struct sockaddr_storage)); + capwap_crypt_change_bio_send(&session->datadtls, ac_bio_send, session); + + /* Remove temp element */ + capwap_itemlist_remove(g_ac.datasessionshandshake, itemlist); + capwap_itemlist_free(itemlist); + + /* Add packet*/ + ac_session_add_packet(session, temp, buffersize, 0, 1); /* Packet already decrypt */ + ac_session_release_reference(session); + } + } else if ((buffersize == CAPWAP_ERROR_SHUTDOWN) || (buffersize == CAPWAP_ERROR_CLOSE)) { + capwap_itemlist_remove(g_ac.datasessionshandshake, itemlist); + capwap_crypt_close(&handshake->dtls); + capwap_crypt_freesession(&handshake->dtls); + capwap_itemlist_free(itemlist); + } + } + } + } +} + +/* Create new session */ +static struct ac_session_t* ac_create_session(struct sockaddr_storage* wtpaddress, struct sockaddr_storage* acaddress, struct capwap_socket* ctrlsock) { + int result; + struct capwap_list_item* itemlist; + struct ac_session_t* session; + + ASSERT(acaddress != NULL); + ASSERT(wtpaddress != NULL); + ASSERT(ctrlsock != NULL); + + /* Create new session */ + itemlist = capwap_itemlist_create(sizeof(struct ac_session_t)); + session = (struct ac_session_t*)itemlist->item; + memset(session, 0, sizeof(struct ac_session_t)); + + session->count = 2; + memcpy(&session->acctrladdress, acaddress, sizeof(struct sockaddr_storage)); + memcpy(&session->wtpctrladdress, wtpaddress, sizeof(struct sockaddr_storage)); + memcpy(&session->ctrlsocket, ctrlsock, sizeof(struct capwap_socket)); + + /* Duplicate state for DFA */ + memcpy(&session->dfa, &g_ac.dfa, sizeof(struct ac_state)); + session->dfa.acipv4list = capwap_array_clone(g_ac.dfa.acipv4list); + session->dfa.acipv6list = capwap_array_clone(g_ac.dfa.acipv6list); + + session->dfa.rfcRetransmitInterval = AC_DEFAULT_RETRANSMIT_INTERVAL; + session->dfa.rfcMaxRetransmit = AC_MAX_RETRANSMIT; + session->dfa.rfcDTLSSessionDelete = AC_DEFAULT_DTLS_SESSION_DELETE; + + /* Add default AC list if empty*/ + if ((session->dfa.acipv4list->count == 0) && (session->dfa.acipv6list->count == 0)) { + if (session->acctrladdress.ss_family == AF_INET) { + struct capwap_acipv4list_element* acip = (struct capwap_acipv4list_element*)capwap_array_get_item_pointer(session->dfa.acipv4list, 0); + memcpy(&acip->address, &((struct sockaddr_in*)&session->acctrladdress)->sin_addr, sizeof(struct in_addr)); + } else if (session->acctrladdress.ss_family == AF_INET6) { + struct capwap_acipv6list_element* acip = (struct capwap_acipv6list_element*)capwap_array_get_item_pointer(session->dfa.acipv6list, 0); + memcpy(&acip->address, &((struct sockaddr_in6*)&session->acctrladdress)->sin6_addr, sizeof(struct in6_addr)); + } + } + + /* Init */ + capwap_event_init(&session->waitpacket); + capwap_lock_init(&session->packetslock); + + session->controlpackets = capwap_list_create(); + session->datapackets = capwap_list_create(); + session->requestfragmentpacket = capwap_array_create(sizeof(struct capwap_packet), 0); + session->responsefragmentpacket = capwap_array_create(sizeof(struct capwap_packet), 0); + session->rxfragmentpacket = capwap_defragment_init_list(); + + session->mtu = g_ac.mtu; + session->state = CAPWAP_IDLE_STATE; + + /* Update session list */ + capwap_lock_enter(&g_ac.sessionslock); + capwap_itemlist_insert_after(g_ac.sessions, NULL, itemlist); + capwap_lock_exit(&g_ac.sessionslock); + + /* Create thread */ + result = pthread_create(&session->threadid, NULL, ac_session_thread, (void*)session); + if (!result) { + pthread_detach(session->threadid); + + /* Notify change session list */ + capwap_event_signal(&g_ac.changesessionlist); + } else { + capwap_logging_debug("Unable create session thread, error code %d", result); + + /* Destroy element */ + capwap_lock_enter(&g_ac.sessionslock); + capwap_itemlist_free(capwap_itemlist_remove(g_ac.sessions, itemlist)); + capwap_lock_exit(&g_ac.sessionslock); + + session = NULL; + } + + return session; +} + +/* Update statistics */ +void ac_update_statistics(void) { + + g_ac.descriptor.station = 0; /* TODO */ + + capwap_lock_enter(&g_ac.sessionslock); + g_ac.descriptor.wtp = g_ac.sessions->count; + capwap_lock_exit(&g_ac.sessionslock); +} + +/* Handler signal */ +static void ac_signal_handler(int signum) { + if ((signum == SIGINT) || (signum == SIGTERM)) { + g_ac.running = 0; + } +} + +/* AC running */ +int ac_execute(void) { + int fdscount = CAPWAP_MAX_SOCKETS * 2; + struct pollfd* fds; + int result = CAPWAP_SUCCESSFUL; + + int index; + int check; + int isctrlsocket = 0; + struct sockaddr_storage recvfromaddr; + struct sockaddr_storage recvtoaddr; + int isrecvpacket = 0; + + struct ac_session_t* session; + struct capwap_socket ctrlsock; + + char buffer[CAPWAP_MAX_PACKET_SIZE]; + int buffersize; + + /* Configure poll struct */ + fds = (struct pollfd*)capwap_alloc(sizeof(struct pollfd) * fdscount); + if (!fds) { + capwap_outofmemory(); + } + + /* Retrive all socket for polling */ + fdscount = capwap_network_set_pollfd(&g_ac.net, fds, fdscount); + ASSERT(fdscount > 0); + + /* Handler signal */ + g_ac.running = 1; + signal(SIGINT, ac_signal_handler); + signal(SIGTERM, ac_signal_handler); + + /* Start discovery thread */ + if (!ac_discovery_start()) { + capwap_free(fds); + capwap_logging_debug("Unable to start discovery thread"); + return AC_ERROR_SYSTEM_FAILER; + } + + /* */ + for (;;) { + /* Receive packet */ + isrecvpacket = 0; + buffersize = sizeof(buffer); + index = capwap_recvfrom(fds, fdscount, buffer, &buffersize, &recvfromaddr, &recvtoaddr, NULL); + if (!g_ac.running) { + break; + } + + /* */ + if (index >= 0) { + /* Detect local address */ + if (recvtoaddr.ss_family == AF_UNSPEC) { + if (capwap_get_localaddress_by_remoteaddress(&recvtoaddr, &recvfromaddr, g_ac.net.bind_interface, (!(g_ac.net.bind_ctrl_flags & CAPWAP_IPV6ONLY_FLAG) ? 1 : 0))) { + struct sockaddr_storage sockinfo; + socklen_t sockinfolen = sizeof(struct sockaddr_storage); + + memset(&sockinfo, 0, sizeof(struct sockaddr_storage)); + if (getsockname(fds[index].fd, (struct sockaddr*)&sockinfo, &sockinfolen) < 0) { + break; + } + + CAPWAP_SET_NETWORK_PORT(&recvtoaddr, CAPWAP_GET_NETWORK_PORT(&sockinfo)); + } + } + + /* Search the AC session */ + isctrlsocket = ((index < (fdscount / 2)) ? 1 : 0); + session = ac_search_session_from_wtpaddress(&recvfromaddr, isctrlsocket); + + if (session) { + /* Add packet*/ + ac_session_add_packet(session, buffer, buffersize, isctrlsocket, 0); + + /* Release reference */ + ac_session_release_reference(session); + } else { + if (isctrlsocket) { + unsigned short sessioncount; + + /* Get current session number */ + capwap_lock_enter(&g_ac.sessionslock); + sessioncount = g_ac.sessions->count; + capwap_lock_exit(&g_ac.sessionslock); + + /* PreParsing packet for reduce a DoS attack */ + check = capwap_sanity_check(isctrlsocket, CAPWAP_UNDEF_STATE, buffer, buffersize, g_ac.enabledtls, 0); + if (check == CAPWAP_PLAIN_PACKET) { + struct capwap_header* header = (struct capwap_header*)buffer; + + /* Accepted only packet without fragmentation */ + if (!IS_FLAG_F_HEADER(header)) { + int headersize = GET_HLEN_HEADER(header) * 4; + if (buffersize >= (headersize + sizeof(struct capwap_control_message))) { + struct capwap_control_message* control = (struct capwap_control_message*)((char*)buffer + headersize); + unsigned long type = ntohl(control->type); + + if (type == CAPWAP_DISCOVERY_REQUEST) { + if (sessioncount < g_ac.descriptor.wtplimit) { + ac_discovery_add_packet(buffer, buffersize, fds[index].fd, &recvfromaddr); + } + } else if (!g_ac.enabledtls && (type == CAPWAP_JOIN_REQUEST)) { + if (sessioncount < g_ac.descriptor.wtplimit) { + /* Retrive socket info */ + capwap_get_network_socket(&g_ac.net, &ctrlsock, fds[index].fd); + + /* Create a new session */ + session = ac_create_session(&recvfromaddr, &recvtoaddr, &ctrlsock); + if (session) { + ac_session_add_packet(session, buffer, buffersize, isctrlsocket, 1); + + /* Release reference */ + ac_session_release_reference(session); + } + } + } + } + } + } else if (check == CAPWAP_DTLS_PACKET) { + /* Need create a new sessione for check if it is a valid DTLS handshake */ + if (sessioncount < g_ac.descriptor.wtplimit) { + /* TODO prevent dos attack add filtering ip for multiple error */ + + /* Retrive socket info */ + capwap_get_network_socket(&g_ac.net, &ctrlsock, fds[index].fd); + + /* Create a new session */ + session = ac_create_session(&recvfromaddr, &recvtoaddr, &ctrlsock); + if (session) { + ac_session_add_packet(session, buffer, buffersize, isctrlsocket, 0); + + /* Release reference */ + ac_session_release_reference(session); + } + } + } + } else { + struct capwap_socket datasocket; + + /* Retrieve session by sessionid of data packet */ + capwap_get_network_socket(&g_ac.net, &datasocket, fds[index].fd); + ac_update_session_from_datapacket(&datasocket, &recvfromaddr, &recvtoaddr, buffer, buffersize); + } + } + } else if (index == CAPWAP_RECV_ERROR_INTR) { + /* Ignore recv */ + continue; + } else if (index == CAPWAP_RECV_ERROR_SOCKET) { + /* Socket close */ + break; + } + } + + /* Terminate discovery thread */ + ac_discovery_stop(); + + /* Close all sessions */ + ac_close_sessions(); + + /* Wait to terminate all sessions */ + for (;;) { + int count; + + capwap_lock_enter(&g_ac.sessionslock); + count = g_ac.sessions->count; + capwap_lock_exit(&g_ac.sessionslock); + + if (!count) { + break; + } + + /* Wait that list is changed */ + capwap_logging_debug("Waiting for %d session terminate", count); + capwap_event_wait(&g_ac.changesessionlist); + } + + /* Free handshark session */ + while (g_ac.datasessionshandshake->first != NULL) { + struct ac_data_session_handshake* handshake = (struct ac_data_session_handshake*)g_ac.datasessionshandshake->first->item; + + if (handshake->dtls.enable) { + capwap_crypt_freesession(&handshake->dtls); + } + + capwap_itemlist_free(capwap_itemlist_remove(g_ac.datasessionshandshake, g_ac.datasessionshandshake->first)); + } + + /* Free memory */ + capwap_free(fds); + return result; +} diff --git a/src/ac/ac_session.c b/src/ac/ac_session.c new file mode 100644 index 0000000..2d11a88 --- /dev/null +++ b/src/ac/ac_session.c @@ -0,0 +1,617 @@ +#include "ac.h" +#include "capwap_dfa.h" +#include "ac_session.h" + +#define PACKET_TIMEOUT -1 +#define DTLS_SHUTDOWN -2 + +static int ac_network_read(struct ac_session_t* session, void* buffer, int length, int* isctrlpacket, struct timeout_control* timeout) { + long indextimer; + long waittimeout; + + ASSERT(session != NULL); + ASSERT(buffer != NULL); + ASSERT(length > 0); + ASSERT(isctrlpacket != NULL); + + for (;;) { + if (session->closesession) { + session->closesession = 0; + return DTLS_SHUTDOWN; + } + + capwap_lock_enter(&session->packetslock); + + if ((session->controlpackets->count > 0) || (session->datapackets->count > 0)) { + int result = 0; + struct capwap_list_item* itempacket; + + *isctrlpacket = ((session->controlpackets->count > 0) ? 1 : 0); + + /* Get packet */ + itempacket = capwap_itemlist_remove_head((*isctrlpacket ? session->controlpackets : session->datapackets)); + capwap_lock_exit(&session->packetslock); + + if (itempacket) { + struct ac_packet* packet = (struct ac_packet*)itempacket->item; + long packetlength = itempacket->itemsize - sizeof(struct ac_packet); + struct capwap_dtls* dtls = (*isctrlpacket ? &session->ctrldtls : &session->datadtls); + + if (!packet->plainbuffer && dtls->enable) { + int oldaction = dtls->action; + + /* Decrypt packet */ + result = capwap_decrypt_packet(dtls, packet->buffer, packetlength, buffer, length); + if (result == CAPWAP_ERROR_AGAIN) { + /* Check is handshake complete */ + if ((oldaction == CAPWAP_DTLS_ACTION_HANDSHAKE) && (dtls->action == CAPWAP_DTLS_ACTION_DATA)) { + if (*isctrlpacket) { + if (session->state == CAPWAP_DTLS_CONNECT_STATE) { + ac_dfa_change_state(session, CAPWAP_JOIN_STATE); + capwap_set_timeout(session->dfa.rfcWaitJoin, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } + } + } + } else if (result == CAPWAP_ERROR_SHUTDOWN) { + if ((oldaction == CAPWAP_DTLS_ACTION_DATA) && (dtls->action == CAPWAP_DTLS_ACTION_SHUTDOWN)) { + result = DTLS_SHUTDOWN; + } + } + } else { + if (packetlength <= length) { + memcpy(buffer, packet->buffer, packetlength); + result = packetlength; + } + } + + /* Free packet */ + capwap_itemlist_free(itempacket); + } + + return result; + } + + capwap_lock_exit(&session->packetslock); + + /* Update timeout */ + capwap_update_timeout(timeout); + waittimeout = capwap_get_timeout(timeout, &indextimer); + if ((waittimeout <= 0) && (indextimer != CAPWAP_TIMER_UNDEF)) { + return PACKET_TIMEOUT; + } + + /* Wait packet */ + capwap_event_wait_timeout(&session->waitpacket, waittimeout); + } + + return 0; +} + +/* */ +static int ac_dfa_execute(struct ac_session_t* session, struct capwap_packet* packet) { + int action = AC_DFA_ACCEPT_PACKET; + + ASSERT(session != NULL); + + /* Execute state */ + switch (session->state) { + case CAPWAP_START_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_START_TO_IDLE_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_IDLE_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_IDLE_TO_DISCOVERY_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_IDLE_TO_DTLS_SETUP_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DISCOVERY_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DISCOVERY_TO_IDLE_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DISCOVERY_TO_SULKING_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DISCOVERY_TO_DTLS_SETUP_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_SULKING_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_SULKING_TO_IDLE_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DTLS_SETUP_STATE: { + action = ac_dfa_state_dtlssetup(session, packet); + break; + } + + case CAPWAP_DTLS_SETUP_TO_IDLE_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DTLS_SETUP_TO_SULKING_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DTLS_SETUP_TO_AUTHORIZE_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_AUTHORIZE_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_AUTHORIZE_TO_DTLS_SETUP_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_AUTHORIZE_TO_DTLS_CONNECT_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_AUTHORIZE_TO_DTLS_TEARDOWN_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DTLS_CONNECT_STATE: { + action = ac_dfa_state_dtlsconnect(session, packet); + break; + } + + case CAPWAP_DTLS_CONNECT_TO_DTLS_TEARDOWN_STATE: { + action = ac_dfa_state_dtlsconnect_to_dtlsteardown(session, packet); + break; + } + + case CAPWAP_DTLS_CONNECT_TO_JOIN_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DTLS_TEARDOWN_STATE: { + action = ac_dfa_state_teardown(session, packet); + break; + } + + case CAPWAP_DTLS_TEARDOWN_TO_IDLE_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DTLS_TEARDOWN_TO_SULKING_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DTLS_TEARDOWN_TO_DEAD_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_JOIN_STATE: { + action = ac_dfa_state_join(session, packet); + break; + } + + case CAPWAP_POSTJOIN_STATE: { + action = ac_dfa_state_postjoin(session, packet); + break; + } + + case CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE: { + action = ac_dfa_state_join_to_dtlsteardown(session, packet); + break; + } + + case CAPWAP_JOIN_TO_IMAGE_DATA_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_JOIN_TO_CONFIGURE_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_IMAGE_DATA_STATE: { + action = ac_dfa_state_imagedata(session, packet); + break; + } + + case CAPWAP_IMAGE_DATA_TO_RESET_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_IMAGE_DATA_TO_DTLS_TEARDOWN_STATE: { + action = ac_dfa_state_imagedata_to_dtlsteardown(session, packet); + break; + } + + case CAPWAP_CONFIGURE_STATE: { + action = ac_dfa_state_configure(session, packet); + break; + } + + case CAPWAP_CONFIGURE_TO_RESET_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_CONFIGURE_TO_DTLS_TEARDOWN_STATE: { + action = ac_dfa_state_configure_to_dtlsteardown(session, packet); + break; + } + + case CAPWAP_CONFIGURE_TO_DATA_CHECK_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_RESET_STATE: { + action = ac_dfa_state_reset(session, packet); + break; + } + + case CAPWAP_RESET_TO_DTLS_TEARDOWN_STATE: { + action = ac_dfa_state_reset_to_dtlsteardown(session, packet); + break; + } + + case CAPWAP_DATA_CHECK_STATE: { + action = ac_dfa_state_datacheck(session, packet); + break; + } + + case CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE: { + action = ac_dfa_state_datacheck_to_dtlsteardown(session, packet); + break; + } + + case CAPWAP_DATA_CHECK_TO_RUN_STATE: { + action = ac_dfa_state_datacheck_to_run(session, packet); + break; + } + + case CAPWAP_RUN_STATE: { + action = ac_dfa_state_run(session, packet); + break; + } + + case CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE: { + action = ac_dfa_state_run_to_dtlsteardown(session, packet); + break; + } + + case CAPWAP_RUN_TO_RESET_STATE: { + action = ac_dfa_state_run_to_reset(session, packet); + break; + } + + case CAPWAP_DEAD_STATE: { + action = ac_dfa_state_dead(session, packet); + break; + } + + default: { + capwap_logging_debug("Unknown action event: %lu", session->state); + break; + } + } + + return action; +} + +static void ac_session_run(struct ac_session_t* session) { + int check; + int length; + int isctrlsocket; + int action = AC_DFA_ACCEPT_PACKET; + char buffer[CAPWAP_MAX_PACKET_SIZE]; + + ASSERT(session != NULL); + + /* Configure DFA */ + if (g_ac.enabledtls) { + action = AC_DFA_NO_PACKET; + ac_dfa_change_state(session, CAPWAP_DTLS_SETUP_STATE); + capwap_set_timeout(session->dfa.rfcWaitDTLS, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } else { + /* Wait Join request */ + ac_dfa_change_state(session, CAPWAP_JOIN_STATE); + capwap_set_timeout(session->dfa.rfcWaitJoin, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } + + while (action != AC_DFA_DEAD) { + /* Get packet */ + if ((action == AC_DFA_ACCEPT_PACKET) || (action == AC_DFA_DROP_PACKET)) { + length = ac_network_read(session, buffer, sizeof(buffer), &isctrlsocket, &session->timeout); + if (length < 0) { + if (length == PACKET_TIMEOUT) { + action = ac_dfa_execute(session, NULL); /* Timeout */ + } else if (length == DTLS_SHUTDOWN) { + action = ac_session_teardown_connection(session); + } + } else if (length > 0) { + /* Accept data packet only in running state */ + if (isctrlsocket || (session->state == CAPWAP_DATA_CHECK_TO_RUN_STATE) || (session->state == CAPWAP_RUN_STATE)) { + /* Check generic capwap packet */ + check = capwap_sanity_check(isctrlsocket, CAPWAP_UNDEF_STATE, buffer, length, 0, 0); + if (check == CAPWAP_PLAIN_PACKET) { + struct capwap_packet packet; + + check = capwap_defragment_packets(&session->wtpctrladdress, buffer, length, session->rxfragmentpacket, &packet); + if (check == CAPWAP_RECEIVE_COMPLETE_PACKET) { + int ignorepacket = 0; + + if (isctrlsocket) { + /* Check for already response to packet */ + if (capwap_recv_retrasmitted_request(&session->ctrldtls, &packet, session->remoteseqnumber, session->lastrecvpackethash, &session->ctrlsocket, session->responsefragmentpacket, &session->acctrladdress, &session->wtpctrladdress)) { + ignorepacket = 1; + } + + /* Check message type */ + if (!capwap_check_message_type(&session->ctrldtls, &packet, session->mtu)) { + ignorepacket = 1; + } + } + + /* */ + if (!ignorepacket && (action == AC_DFA_ACCEPT_PACKET)) { + memcpy(&packet.socket, (isctrlsocket ? &session->ctrlsocket : &session->datasocket), sizeof(struct capwap_socket)); + action = ac_dfa_execute(session, &packet); + } + + /* Free packet */ + capwap_free_packet(&packet); + } else if (check != CAPWAP_REQUEST_MORE_FRAGMENT) { + /* Discard fragments */ + capwap_defragment_remove_sender(session->rxfragmentpacket, &session->wtpctrladdress); + } + } + } + } + } else { + action = ac_dfa_execute(session, NULL); + } + } + + /* Release reference session */ + if (!ac_session_release_reference(session)) { + capwap_logging_debug("Reference session is > 0 to exit thread"); + } +} + +/* Change WTP state machine */ +void ac_dfa_change_state(struct ac_session_t* session, int state) { + ASSERT(session != NULL); + + if (state != session->state) { +#ifdef DEBUG + char sessionname[33]; + capwap_sessionid_printf(&session->sessionid, sessionname); + capwap_logging_debug("Session AC %s change state from %s to %s", sessionname, capwap_dfa_getname(session->state), capwap_dfa_getname(state)); +#endif + + session->state = state; + } +} + +/* Teardown connection */ +int ac_session_teardown_connection(struct ac_session_t* session) { + ASSERT(session != NULL); + + /* Close DTSL Control */ + if (session->ctrldtls.enable) { + capwap_crypt_close(&session->ctrldtls); + } + + /* Close DTLS Data */ + if (session->datadtls.enable) { + capwap_crypt_close(&session->datadtls); + } + + /* */ + capwap_killall_timeout(&session->timeout); + capwap_set_timeout(session->dfa.rfcDTLSSessionDelete, &session->timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + ac_dfa_change_state(session, CAPWAP_DTLS_TEARDOWN_STATE); + return AC_DFA_DROP_PACKET; +} + +/* Release reference of session */ +int ac_session_release_reference(struct ac_session_t* session) { + int remove = 0; + struct capwap_list_item* search; + + ASSERT(session != NULL); + + capwap_lock_enter(&g_ac.sessionslock); + + session->count--; + if (!session->count) { + search = g_ac.sessions->first; + while (search != NULL) { + struct ac_session_t* item = (struct ac_session_t*)search->item; + if (session == item) { + /* Free DTSL Control */ + capwap_crypt_freesession(&session->ctrldtls); + + /* Free DTLS Data */ + capwap_crypt_freesession(&session->datadtls); + + /* Free resource */ + capwap_event_destroy(&session->waitpacket); + capwap_lock_exit(&session->packetslock); + capwap_list_free(session->controlpackets); + capwap_list_free(session->datapackets); + capwap_defragment_free_list(session->rxfragmentpacket); + + /* Free fragments packet */ + capwap_fragment_free(session->requestfragmentpacket); + capwap_fragment_free(session->responsefragmentpacket); + capwap_array_free(session->requestfragmentpacket); + capwap_array_free(session->responsefragmentpacket); + + /* Free DFA resource */ + capwap_array_free(session->dfa.acipv4list); + capwap_array_free(session->dfa.acipv6list); + + /* Remove item from list */ + remove = 1; + capwap_itemlist_free(capwap_itemlist_remove(g_ac.sessions, search)); + capwap_event_signal(&g_ac.changesessionlist); + + break; + } + + search = search->next; + } + } + + capwap_lock_exit(&g_ac.sessionslock); + + return remove; +} + +/* */ +void* ac_session_thread(void* param) { + ASSERT(param != NULL); + + capwap_logging_debug("Session start"); + ac_session_run((struct ac_session_t*)param); + capwap_logging_debug("Session end"); + + /* Thread exit */ + pthread_exit(NULL); + return NULL; +} + +/* */ +void ac_get_control_information(struct capwap_list* controllist) { + struct capwap_list* addrlist; + struct capwap_list_item* item; + + ASSERT(controllist != NULL); + + /* Detect local address */ + addrlist = capwap_list_create(); + capwap_interface_list(&g_ac.net, addrlist); + + /* Prepare control list */ + for (item = addrlist->first; item != NULL; item = item->next) { + struct capwap_list_item* itemcontrol; + struct ac_session_control* sessioncontrol; + struct sockaddr_storage* address = (struct sockaddr_storage*)item->item; + + /* */ + itemcontrol = capwap_itemlist_create(sizeof(struct ac_session_control)); + sessioncontrol = (struct ac_session_control*)itemcontrol->item; + memcpy(&sessioncontrol->localaddress, address, sizeof(struct sockaddr_storage)); + sessioncontrol->count = 0; + + /* Add */ + capwap_itemlist_insert_after(controllist, NULL, itemcontrol); + } + + /* Free local address list */ + capwap_list_free(addrlist); + + /* */ + capwap_lock_enter(&g_ac.sessionslock); + + /* Get wtp count from any local address */ + for (item = controllist->first; item != NULL; item = item->next) { + struct capwap_list_item* search; + struct ac_session_control* sessioncontrol = (struct ac_session_control*)item->item; + + for (search = g_ac.sessions->first; search != NULL; search = search->next) { + struct ac_session_t* session = (struct ac_session_t*)search->item; + + if (!capwap_compare_ip(&session->acctrladdress, &sessioncontrol->localaddress)) { + sessioncontrol->count++; + } + } + } + + /* */ + capwap_lock_exit(&g_ac.sessionslock); +} + +/* */ +void ac_free_reference_last_request(struct ac_session_t* session) { + ASSERT(session); + + capwap_fragment_free(session->requestfragmentpacket); +} + +/* */ +void ac_free_reference_last_response(struct ac_session_t* session) { + ASSERT(session); + + capwap_fragment_free(session->responsefragmentpacket); + memset(&session->lastrecvpackethash[0], 0, sizeof(session->lastrecvpackethash)); +} diff --git a/src/ac/ac_session.h b/src/ac/ac_session.h new file mode 100644 index 0000000..75d2af6 --- /dev/null +++ b/src/ac/ac_session.h @@ -0,0 +1,112 @@ +#ifndef __AC_SESSION_HEADER__ +#define __AC_SESSION_HEADER__ + +#include "capwap_dtls.h" + +#define AC_DFA_NO_PACKET 0 +#define AC_DFA_ACCEPT_PACKET 1 +#define AC_DFA_DROP_PACKET 2 +#define AC_DFA_DEAD 3 + +/* AC packet */ +struct ac_packet { + int plainbuffer; + char buffer[0]; +}; + +/* */ +struct ac_session_control { + struct sockaddr_storage localaddress; + unsigned short count; +}; + +/* AC sessions */ +struct ac_session_t { + struct ac_state dfa; + + unsigned long count; + struct sockaddr_storage acctrladdress; + struct sockaddr_storage acdataaddress; + struct sockaddr_storage wtpctrladdress; + struct sockaddr_storage wtpdataaddress; + struct capwap_socket ctrlsocket; + struct capwap_socket datasocket; + struct timeout_control timeout; + + struct capwap_sessionid_element sessionid; + unsigned short binding; + + struct capwap_dtls ctrldtls; + struct capwap_dtls datadtls; + + int closesession; + pthread_t threadid; + + capwap_event_t waitpacket; + capwap_lock_t packetslock; + struct capwap_list* controlpackets; + struct capwap_list* datapackets; + + unsigned char localseqnumber; + unsigned char remoteseqnumber; + unsigned short mtu; + unsigned short fragmentid; + capwap_fragment_list* rxfragmentpacket; + capwap_fragment_packet_array* requestfragmentpacket; + capwap_fragment_packet_array* responsefragmentpacket; + unsigned char lastrecvpackethash[16]; + + unsigned long state; + + struct capwap_imageidentifier_element startupimage; +}; + +void* ac_session_thread(void* param); +int ac_session_teardown_connection(struct ac_session_t* session); +int ac_session_release_reference(struct ac_session_t* session); + +void ac_dfa_change_state(struct ac_session_t* session, int state); + +void ac_get_control_information(struct capwap_list* controllist); + +void ac_free_reference_last_request(struct ac_session_t* session); +void ac_free_reference_last_response(struct ac_session_t* session); + +/* */ +int ac_dfa_state_join(struct ac_session_t* session, struct capwap_packet* packet); +int ac_dfa_state_postjoin(struct ac_session_t* session, struct capwap_packet* packet); +int ac_dfa_state_join_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet); + +/* */ +int ac_bio_send(struct capwap_dtls* dtls, char* buffer, int length, void* param); +int ac_dfa_state_dtlssetup(struct ac_session_t* session, struct capwap_packet* packet); +int ac_dfa_state_dtlsconnect(struct ac_session_t* session, struct capwap_packet* packet); +int ac_dfa_state_dtlsconnect_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet); + +/* */ +int ac_dfa_state_configure(struct ac_session_t* session, struct capwap_packet* packet); +int ac_dfa_state_configure_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet); + +/* */ +int ac_dfa_state_datacheck(struct ac_session_t* session, struct capwap_packet* packet); +int ac_dfa_state_datacheck_to_run(struct ac_session_t* session, struct capwap_packet* packet); +int ac_dfa_state_datacheck_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet); + +/* */ +int ac_dfa_state_imagedata(struct ac_session_t* session, struct capwap_packet* packet); +int ac_dfa_state_imagedata_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet); + +/* */ +int ac_dfa_state_run(struct ac_session_t* session, struct capwap_packet* packet); +int ac_dfa_state_run_to_reset(struct ac_session_t* session, struct capwap_packet* packet); +int ac_dfa_state_run_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet); + +/* */ +int ac_dfa_state_reset(struct ac_session_t* session, struct capwap_packet* packet); +int ac_dfa_state_reset_to_dtlsteardown(struct ac_session_t* session, struct capwap_packet* packet); + +/* */ +int ac_dfa_state_teardown(struct ac_session_t* session, struct capwap_packet* packet); +int ac_dfa_state_dead(struct ac_session_t* session, struct capwap_packet* packet); + +#endif /* __AC_SESSION_HEADER__ */ diff --git a/src/common/capwap.c b/src/common/capwap.c new file mode 100644 index 0000000..53dd90b --- /dev/null +++ b/src/common/capwap.c @@ -0,0 +1,151 @@ +#include "capwap.h" + +/* Helper exit */ +void capwap_exit(int errorcode) { + exit(errorcode); +} + +/* Helper timeout calc */ +void capwap_init_timeout(struct timeout_control* timeout) { + ASSERT(timeout); + + memset(timeout, 0, sizeof(struct timeout_control)); +} + +void capwap_update_timeout(struct timeout_control* timeout) { + int i; + struct timeval now; + + ASSERT(timeout); + + gettimeofday(&now, NULL); + + for (i = 0; i < CAPWAP_MAX_TIMER; i++) { + if (timeout->items[i].enable && (timeout->items[i].delta >= 0)) { + timeout->items[i].delta = (timeout->items[i].timestop.tv_sec - now.tv_sec) * 1000 + (timeout->items[i].timestop.tv_usec - now.tv_usec) / 1000; + if (timeout->items[i].delta < 0) { + timeout->items[i].delta = 0; + } else if (timeout->items[i].delta > timeout->items[i].durate) { + /* Changed system time */ + timeout->items[i].delta = timeout->items[i].durate; + memcpy(&timeout->items[i].timestop, &now, sizeof(struct timeval)); + timeout->items[i].timestop.tv_sec += timeout->items[i].durate; + } + } + } +} + +long capwap_get_timeout(struct timeout_control* timeout, long* index) { + long i; + long delta = 0; + + ASSERT(timeout != NULL); + ASSERT(index != NULL); + + *index = CAPWAP_TIMER_UNDEF; + for (i = 0; i < CAPWAP_MAX_TIMER; i++) { + if (timeout->items[i].enable) { + if (timeout->items[i].delta <= 0) { + *index = i; + delta = 0; + break; + } else if (!delta || (delta > timeout->items[i].delta)) { + *index = i; + delta = timeout->items[i].delta; + } + } + } + + return delta; +} + +int capwap_is_enable_timeout(struct timeout_control* timeout, unsigned long index) { + ASSERT(timeout != NULL); + ASSERT(index < CAPWAP_MAX_TIMER); + + return (timeout->items[index].enable ? 1 : 0); +} + +int capwap_is_timeout(struct timeout_control* timeout, unsigned long index) { + ASSERT(timeout != NULL); + ASSERT(index < CAPWAP_MAX_TIMER); + + if (timeout->items[index].enable && (timeout->items[index].delta <= 0)) { + return 1; + } + + return 0; +} + +void capwap_set_timeout(unsigned long value, struct timeout_control* timeout, unsigned long index) { + ASSERT(timeout != NULL); + ASSERT(index < CAPWAP_MAX_TIMER); + + /* Set timeout in ms */ + timeout->items[index].enable = 1; + timeout->items[index].delta = value * 1000; + timeout->items[index].durate = value * 1000; + gettimeofday(&timeout->items[index].timestop, NULL); + timeout->items[index].timestop.tv_sec += value; +} + +void capwap_kill_timeout(struct timeout_control* timeout, unsigned long index) { + ASSERT(timeout != NULL); + ASSERT(index < CAPWAP_MAX_TIMER); + + timeout->items[index].enable = 0; +} + +void capwap_killall_timeout(struct timeout_control* timeout) { + long i; + + ASSERT(timeout != NULL); + + for (i = 0; i < CAPWAP_MAX_TIMER; i++) { + timeout->items[i].enable = 0; + } +} + +/* Init randon generator */ +void capwap_init_rand(void) { + srand(time(NULL)); +} + +/* Get random number */ +int capwap_get_rand(int max) { + if ((max < 0) || (max > RAND_MAX)) { + max = RAND_MAX; + } + + return (rand() % max); +} + +/* Duplicate string */ +char* capwap_duplicate_string(const char* source) { + char* clone; + + ASSERT(source != NULL); + + clone = capwap_alloc(sizeof(char) * (strlen(source) + 1)); + if (!clone) { + capwap_outofmemory(); + } + + strcpy(clone, source); + return clone; +} + +/* Buffer clone */ +void* capwap_clone(void* buffer, int buffersize) { + void* bufferclone; + + ASSERT(buffer != NULL); + ASSERT(buffersize > 0); + + bufferclone = capwap_alloc(buffersize); + if (!bufferclone) { + capwap_outofmemory(); + } + + return memcpy(bufferclone, buffer, buffersize); +} diff --git a/src/common/capwap.h b/src/common/capwap.h new file mode 100644 index 0000000..c162c0f --- /dev/null +++ b/src/common/capwap.h @@ -0,0 +1,101 @@ +#ifndef __CAPWAP_HEADER__ +#define __CAPWAP_HEADER__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//TODO: +//#ifdef NATIVE_UDPLITE_HEADER +//#include +//#else +//#define IPPROTO_UDPLITE 136 +#define SOL_UDPLITE 136 +#define UDPLITE_SEND_CSCOV 10 +//#endif + +/* Endian */ +#ifdef WIN32 + #define CAPWAP_LITTLE_ENDIAN +#else + #if __BYTE_ORDER == __BIG_ENDIAN + #define CAPWAP_BIG_ENDIAN + #else + #define CAPWAP_LITTLE_ENDIAN + #endif +#endif + +/* Min & Max */ +#ifndef max + #define max(a,b) ((a) >= (b) ? (a) : (b)) +#endif + +#ifndef min + #define min(a,b) ((a) <= (b) ? (a) : (b)) +#endif + +/* config */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* standard include */ +#include "capwap_logging.h" +#include "capwap_debug.h" +#include "capwap_error.h" + +/* Helper exit */ +void capwap_exit(int errorcode); + +/* Random generator */ +void capwap_init_rand(void); +int capwap_get_rand(int max); + +/* Helper timeout calc */ +struct timeout_control_item { + int enable; + long delta; + unsigned long durate; + struct timeval timestop; +}; + +#define CAPWAP_TIMER_UNDEF -1 +#define CAPWAP_TIMER_CONTROL_CONNECTION 0 +#define CAPWAP_TIMER_CONTROL_ECHO 1 +#define CAPWAP_TIMER_DATA_KEEPALIVE 2 +#define CAPWAP_TIMER_DATA_KEEPALIVEDEAD 3 +#define CAPWAP_MAX_TIMER 4 + +struct timeout_control { + struct timeout_control_item items[CAPWAP_MAX_TIMER]; +}; + +void capwap_init_timeout(struct timeout_control* timeout); +long capwap_get_timeout(struct timeout_control* timeout, long* index); +void capwap_update_timeout(struct timeout_control* timeout); +void capwap_set_timeout(unsigned long value, struct timeout_control* timeout, unsigned long index); +void capwap_kill_timeout(struct timeout_control* timeout, unsigned long index); +void capwap_killall_timeout(struct timeout_control* timeout); +int capwap_is_enable_timeout(struct timeout_control* timeout, unsigned long index); +int capwap_is_timeout(struct timeout_control* timeout, unsigned long index); + +/* */ +#define capwap_outofmemory() capwap_logging_fatal("Out of memory %s(%d)", __FILE__, __LINE__); \ + capwap_exit(CAPWAP_OUT_OF_MEMORY); + +/* Helper buffer copy */ +char* capwap_duplicate_string(const char* source); +void* capwap_clone(void* buffer, int buffersize); + +#endif /* __CAPWAP_HEADER__ */ diff --git a/src/common/capwap_array.c b/src/common/capwap_array.c new file mode 100644 index 0000000..96d4a33 --- /dev/null +++ b/src/common/capwap_array.c @@ -0,0 +1,92 @@ +#include "capwap.h" +#include "capwap_array.h" + +/* */ +struct capwap_array* capwap_array_create(unsigned short itemsize, unsigned long initcount) { + struct capwap_array* array; + + ASSERT(itemsize > 0); + + array = (struct capwap_array*)capwap_alloc(sizeof(struct capwap_array)); + if (!array) { + capwap_outofmemory(); + } + + memset(array, 0, sizeof(struct capwap_array)); + array->itemsize = itemsize; + if (initcount > 0) { + capwap_array_resize(array, initcount); + } + + return array; +} + +/* */ +struct capwap_array* capwap_array_clone(struct capwap_array* array) { + unsigned long i; + struct capwap_array* clone; + + ASSERT (array != NULL); + + /* Clone array e items */ + clone = capwap_array_create(array->itemsize, array->count); + for (i = 0; i < array->count; i++) { + memcpy(capwap_array_get_item_pointer(clone, i), capwap_array_get_item_pointer(array, i), array->itemsize); + } + + return clone; +} + +/* */ +void capwap_array_free(struct capwap_array* array) { + ASSERT(array != NULL); + + if (array->buffer) { + capwap_free(array->buffer); + } + + capwap_free(array); +} + +/* */ +void* capwap_array_get_item_pointer(struct capwap_array* array, unsigned long pos) { + ASSERT(array != NULL); + ASSERT((array->count == 0) || (array->buffer != NULL)); + + if (pos >= array->count) { + capwap_array_resize(array, pos + 1); + } + + return (void*)(((char*)array->buffer) + array->itemsize * pos); +} + +/* */ +void capwap_array_resize(struct capwap_array* array, unsigned long count) { + void* newbuffer = NULL; + + ASSERT(array != NULL); + ASSERT(array->itemsize > 0); + + if (array->count == count) { + return; + } + + if (count > 0) { + newbuffer = capwap_alloc(array->itemsize * count); + if (!newbuffer) { + capwap_outofmemory(); + } + } + + if (array->buffer) { + if (newbuffer != NULL) { + memcpy(newbuffer, array->buffer, array->itemsize * min(array->count, count)); + } + + capwap_free(array->buffer); + } + + array->buffer = newbuffer; + array->count = count; +} + diff --git a/src/common/capwap_array.h b/src/common/capwap_array.h new file mode 100644 index 0000000..d6e2e5d --- /dev/null +++ b/src/common/capwap_array.h @@ -0,0 +1,21 @@ +#ifndef __CAPWAP_ARRAY_HEADER__ +#define __CAPWAP_ARRAY_HEADER__ + +struct capwap_array { + void* buffer; + unsigned short itemsize; + unsigned long count; +}; + +struct capwap_array* capwap_array_create(unsigned short itemsize, unsigned long initcount); +struct capwap_array* capwap_array_clone(struct capwap_array* array); +void capwap_array_free(struct capwap_array* array); +void* capwap_array_get_item_pointer(struct capwap_array* array, unsigned long pos); +void capwap_array_resize(struct capwap_array* array, unsigned long count); + +/* Helper */ +#define capwap_array_getitem(x, y, z) *((z*)capwap_array_get_item_pointer((x), (y))) +#define capwap_array_setnewitem(x, y, z) *((z*)capwap_array_get_item_pointer((x), (x)->count)) = (y) + + +#endif /* __CAPWAP_ARRAY_HEADER__ */ diff --git a/src/common/capwap_debug.c b/src/common/capwap_debug.c new file mode 100644 index 0000000..538c032 --- /dev/null +++ b/src/common/capwap_debug.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "capwap_logging.h" + +#define CANARY 0xaaaaaaaa +#define BACKTRACE_BUFFER 256 + +#ifndef DEBUG_BREAKPOINT +#define DEBUG_BREAKPOINT() __asm__("int3") +#endif + +/* Memory block */ +struct capwap_memory_block { + void* item; + size_t size; + const char* file; + int line; + void* backtrace[BACKTRACE_BUFFER]; + int backtrace_count; + struct capwap_memory_block* next; +}; + +static struct capwap_memory_block* g_memoryblocks = NULL; + +/* Alloc memory block */ +void* capwap_alloc_debug(size_t size, const char* file, const int line) { + struct capwap_memory_block* block; + + /* Request size > 0 */ + if (size <= 0) { + capwap_logging_debug("%s(%d): Invalid memory size %d", file, line, size); + DEBUG_BREAKPOINT(); + return NULL; + } + + /* Alloc block with memory block and canary */ + block = (struct capwap_memory_block*)malloc(sizeof(struct capwap_memory_block) + size + 4); + if (!block) { + capwap_logging_debug("Out of memory %s(%d)", __FILE__, __LINE__); + DEBUG_BREAKPOINT(); + return NULL; + } + + /* Info memory block */ + block->item = (void*)(((char*)block) + sizeof(struct capwap_memory_block)); + block->size = size; + block->file = file; + block->line = line; + block->backtrace_count = backtrace(block->backtrace, BACKTRACE_BUFFER); + block->next = g_memoryblocks; + + /* Canary */ + *((unsigned long*)(((char*)block->item) + block->size)) = CANARY; + + g_memoryblocks = block; + + return block->item; +} + +/* Free memory block */ +void capwap_free_debug(void* p, const char* file, const int line) { + struct capwap_memory_block* block; + struct capwap_memory_block* findblock; + struct capwap_memory_block* prevblock; + + if (!p) { + capwap_logging_debug("%s(%d): Free NULL pointer", file, line); + DEBUG_BREAKPOINT(); + return; + } + + /* Memory block */ + if ((size_t)p <= sizeof(struct capwap_memory_block)) { + capwap_logging_debug("%s(%d): Invalid pointer", file, line); + DEBUG_BREAKPOINT(); + return; + } + + block = (struct capwap_memory_block*)((char*)p - sizeof(struct capwap_memory_block)); + if (block->item != p) { + capwap_logging_debug("%s(%d): Invalid pointer", file, line); + DEBUG_BREAKPOINT(); + return; + } + + /* Check canary */ + if (*((unsigned long*)(((char*)block->item) + block->size)) != CANARY) { + capwap_logging_debug("%s(%d): Invalid canary allocted in %s(%d)", file, line, block->file, block->line); + DEBUG_BREAKPOINT(); + return; + } + + /* Find memory block */ + prevblock = NULL; + findblock = g_memoryblocks; + while (findblock != NULL) { + if (findblock == block) { + if (!prevblock) { + g_memoryblocks = block->next; + } else { + prevblock->next = block->next; + } + + /* Invalidate block */ + memset(block, 0, sizeof(struct capwap_memory_block)); + free(block); + return; + } + + /* Next */ + prevblock = findblock; + findblock = findblock->next; + } + + capwap_logging_debug("%s(%d): Unable to find memory block", file, line); +} + +/* Dump memory alloced */ +void capwap_dump_memory(void) { + char** backtrace_functions; + struct capwap_memory_block* findblock; + + findblock = g_memoryblocks; + while (findblock != NULL) { + capwap_logging_debug("%s(%d): block at %p, %d bytes long", findblock->file, findblock->line, findblock->item, findblock->size); + + backtrace_functions = backtrace_symbols(findblock->backtrace, findblock->backtrace_count); + if (backtrace_functions) { + int j; + + /* Skipping capwap_alloc_debug function print out */ + for (j = 1; j < findblock->backtrace_count; j++) { + capwap_logging_debug("\t%s", backtrace_functions[j]); + } + + free(backtrace_functions); + } + + + /* Next */ + findblock = findblock->next; + } +} + +/* Check if all memory is free */ +int capwap_check_memory_leak(int verbose) { + if ((g_memoryblocks != NULL) && (verbose != 0)) { + capwap_logging_debug("*** Detected memory leaks ! ***"); + capwap_dump_memory(); + capwap_logging_debug("*******************************"); + } + + return ((g_memoryblocks != NULL) ? 1 : 0); +} diff --git a/src/common/capwap_debug.h b/src/common/capwap_debug.h new file mode 100644 index 0000000..e57dd76 --- /dev/null +++ b/src/common/capwap_debug.h @@ -0,0 +1,37 @@ +#ifndef __CAPWAP_DEBUG_HEADER__ +#define __CAPWAP_DEBUG_HEADER__ + +#ifdef DEBUG + +#define DEBUG_BREAKPOINT() __asm__("int3") + +#define ASSERT(expr) if (!(expr)) { \ + capwap_logging_fatal("Assertion failed \'%s\': %s(%d)", #expr, __FILE__, __LINE__); \ + DEBUG_BREAKPOINT(); \ + } + +/* Custom memory management */ +#define capwap_alloc(x) capwap_alloc_debug(x, __FILE__, __LINE__) +void* capwap_alloc_debug(size_t size, const char* file, const int line); +#define capwap_free(x) capwap_free_debug(x, __FILE__, __LINE__) +void capwap_free_debug(void* p, const char* file, const int line); + +int capwap_check_memory_leak(int verbose); +void capwap_dump_memory(void); + +#else + +#define DEBUG_BREAKPOINT() + +#define ASSERT(expr) + +/* Standard memory management */ +#define capwap_alloc(x) (void*)malloc(x) +#define capwap_free(x) free(x) +#define capwap_check_memory_leak(x) (0) +#define capwap_dump_memory() (0) + +#endif + +#endif /* __CAPWAP_DEBUG_HEADER__ */ + diff --git a/src/common/capwap_dfa.c b/src/common/capwap_dfa.c new file mode 100644 index 0000000..253c3eb --- /dev/null +++ b/src/common/capwap_dfa.c @@ -0,0 +1,61 @@ +#include "capwap.h" +#include "capwap_dfa.h" + +static char* l_nameofstate[] = { + "START", /* CAPWAP_START_STATE */ + "START_TO_IDLE", /* CAPWAP_START_TO_IDLE_STATE */ + "IDLE", /* CAPWAP_IDLE_STATE */ + "IDLE_TO_DISCOVERY", /* CAPWAP_IDLE_TO_DISCOVERY_STATE */ + "IDLE_TO_DTLS_SETUP", /* CAPWAP_IDLE_TO_DTLS_SETUP_STATE */ + "DISCOVERY", /* CAPWAP_DISCOVERY_STATE */ + "DISCOVERY_TO_IDLE", /* CAPWAP_DISCOVERY_TO_IDLE_STATE */ + "DISCOVERY_TO_SULKING", /* CAPWAP_DISCOVERY_TO_SULKING_STATE */ + "DISCOVERY_TO_DTLS_SETUP", /* CAPWAP_DISCOVERY_TO_DTLS_SETUP_STATE */ + "SULKING", /* CAPWAP_SULKING_STATE */ + "SULKING_TO_IDLE", /* CAPWAP_SULKING_TO_IDLE_STATE */ + "DTLS_SETUP", /* CAPWAP_DTLS_SETUP_STATE */ + "DTLS_SETUP_TO_IDLE", /* CAPWAP_DTLS_SETUP_TO_IDLE_STATE */ + "DTLS_SETUP_TO_SULKING", /* CAPWAP_DTLS_SETUP_TO_SULKING_STATE */ + "DTLS_SETUP_TO_AUTHORIZE", /* CAPWAP_DTLS_SETUP_TO_AUTHORIZE_STATE */ + "AUTHORIZE", /* CAPWAP_AUTHORIZE_STATE */ + "AUTHORIZE_TO_DTLS_SETUP", /* CAPWAP_AUTHORIZE_TO_DTLS_SETUP_STATE */ + "AUTHORIZE_TO_DTLS_CONNECT", /* CAPWAP_AUTHORIZE_TO_DTLS_CONNECT_STATE */ + "AUTHORIZE_TO_DTLS_TEARDOWN", /* CAPWAP_AUTHORIZE_TO_DTLS_TEARDOWN_STATE */ + "DTLS_CONNECT", /* CAPWAP_DTLS_CONNECT_STATE */ + "DTLS_CONNECT_TO_DTLS_TEARDOWN", /* CAPWAP_DTLS_CONNECT_TO_DTLS_TEARDOWN_STATE */ + "DTLS_CONNECT_TO_JOIN", /* CAPWAP_DTLS_CONNECT_TO_JOIN_STATE */ + "DTLS_TEARDOWN", /* CAPWAP_DTLS_TEARDOWN_STATE */ + "DTLS_TEARDOWN_TO_IDLE", /* CAPWAP_DTLS_TEARDOWN_TO_IDLE_STATE */ + "DTLS_TEARDOWN_TO_SULKING", /* CAPWAP_DTLS_TEARDOWN_TO_SULKING_STATE */ + "DTLS_TEARDOWN_TO_DEAD", /* CAPWAP_DTLS_TEARDOWN_TO_DEAD_STATE */ + "JOIN", /* CAPWAP_JOIN_STATE */ + "POST_JOIN", /* CAPWAP_POSTJOIN_STATE */ + "JOIN_TO_DTLS_TEARDOWN", /* CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE */ + "JOIN_TO_IMAGE_DATA", /* CAPWAP_JOIN_TO_IMAGE_DATA_STATE */ + "JOIN_TO_CONFIGURE", /* CAPWAP_JOIN_TO_CONFIGURE_STATE */ + "IMAGE_DATA", /* CAPWAP_IMAGE_DATA_STATE */ + "IMAGE_DATA_TO_RESET", /* CAPWAP_IMAGE_DATA_TO_RESET_STATE */ + "IMAGE_DATA_TO_DTLS_TEARDOWN", /* CAPWAP_IMAGE_DATA_TO_DTLS_TEARDOWN_STATE */ + "CONFIGURE", /* CAPWAP_CONFIGURE_STATE */ + "CONFIGURE_TO_RESET", /* CAPWAP_CONFIGURE_TO_RESET_STATE */ + "CONFIGURE_TO_DTLS_TEARDOWN", /* CAPWAP_CONFIGURE_TO_DTLS_TEARDOWN_STATE */ + "CONFIGURE_TO_DATA_CHECK", /* CAPWAP_CONFIGURE_TO_DATA_CHECK_STATE */ + "RESET", /* CAPWAP_RESET_STATE */ + "RESET_TO_DTLS_TEARDOWN", /* CAPWAP_RESET_TO_DTLS_TEARDOWN_STATE */ + "DATA_CHECK", /* CAPWAP_DATA_CHECK_STATE */ + "DATA_CHECK_TO_DTLS_TEARDOWN", /* CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE */ + "DATA_CHECK_TO_RUN", /* CAPWAP_DATA_CHECK_TO_RUN_STATE */ + "RUN", /* CAPWAP_RUN_STATE */ + "RUN_TO_DTLS_TEARDOWN", /* CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE */ + "RUN_TO_RESET", /* CAPWAP_RUN_TO_RESET_STATE */ + "DEAD" /* CAPWAP_DEAD_STATE */ +}; + +/* */ +char* capwap_dfa_getname(int state) { + if ((state < 0) || (state > CAPWAP_LAST_STATE)) { + return ""; + } + + return l_nameofstate[state]; +} diff --git a/src/common/capwap_dfa.h b/src/common/capwap_dfa.h new file mode 100644 index 0000000..9a251e7 --- /dev/null +++ b/src/common/capwap_dfa.h @@ -0,0 +1,57 @@ +#ifndef __CAPWAP_DFA_HEADER__ +#define __CAPWAP_DFA_HEADER__ + +#define CAPWAP_UNDEF_STATE -1 +#define CAPWAP_START_STATE 0 +#define CAPWAP_START_TO_IDLE_STATE 1 +#define CAPWAP_IDLE_STATE 2 +#define CAPWAP_IDLE_TO_DISCOVERY_STATE 3 +#define CAPWAP_IDLE_TO_DTLS_SETUP_STATE 4 +#define CAPWAP_DISCOVERY_STATE 5 +#define CAPWAP_DISCOVERY_TO_IDLE_STATE 6 +#define CAPWAP_DISCOVERY_TO_SULKING_STATE 7 +#define CAPWAP_DISCOVERY_TO_DTLS_SETUP_STATE 8 +#define CAPWAP_SULKING_STATE 9 +#define CAPWAP_SULKING_TO_IDLE_STATE 10 +#define CAPWAP_DTLS_SETUP_STATE 11 +#define CAPWAP_DTLS_SETUP_TO_IDLE_STATE 12 +#define CAPWAP_DTLS_SETUP_TO_SULKING_STATE 13 +#define CAPWAP_DTLS_SETUP_TO_AUTHORIZE_STATE 14 +#define CAPWAP_AUTHORIZE_STATE 15 +#define CAPWAP_AUTHORIZE_TO_DTLS_SETUP_STATE 16 +#define CAPWAP_AUTHORIZE_TO_DTLS_CONNECT_STATE 17 +#define CAPWAP_AUTHORIZE_TO_DTLS_TEARDOWN_STATE 18 +#define CAPWAP_DTLS_CONNECT_STATE 19 +#define CAPWAP_DTLS_CONNECT_TO_DTLS_TEARDOWN_STATE 20 +#define CAPWAP_DTLS_CONNECT_TO_JOIN_STATE 21 +#define CAPWAP_DTLS_TEARDOWN_STATE 22 +#define CAPWAP_DTLS_TEARDOWN_TO_IDLE_STATE 23 +#define CAPWAP_DTLS_TEARDOWN_TO_SULKING_STATE 24 +#define CAPWAP_DTLS_TEARDOWN_TO_DEAD_STATE 25 +#define CAPWAP_JOIN_STATE 26 +#define CAPWAP_POSTJOIN_STATE 27 +#define CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE 28 +#define CAPWAP_JOIN_TO_IMAGE_DATA_STATE 29 +#define CAPWAP_JOIN_TO_CONFIGURE_STATE 30 +#define CAPWAP_IMAGE_DATA_STATE 31 +#define CAPWAP_IMAGE_DATA_TO_RESET_STATE 32 +#define CAPWAP_IMAGE_DATA_TO_DTLS_TEARDOWN_STATE 33 +#define CAPWAP_CONFIGURE_STATE 34 +#define CAPWAP_CONFIGURE_TO_RESET_STATE 35 +#define CAPWAP_CONFIGURE_TO_DTLS_TEARDOWN_STATE 36 +#define CAPWAP_CONFIGURE_TO_DATA_CHECK_STATE 37 +#define CAPWAP_RESET_STATE 38 +#define CAPWAP_RESET_TO_DTLS_TEARDOWN_STATE 39 +#define CAPWAP_DATA_CHECK_STATE 40 +#define CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE 41 +#define CAPWAP_DATA_CHECK_TO_RUN_STATE 42 +#define CAPWAP_RUN_STATE 43 +#define CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE 44 +#define CAPWAP_RUN_TO_RESET_STATE 45 +#define CAPWAP_DEAD_STATE 46 +#define CAPWAP_LAST_STATE 46 + +/* */ +char* capwap_dfa_getname(int state); + +#endif /* __CAPWAP_DFA_HEADER__ */ diff --git a/src/common/capwap_dtls.c b/src/common/capwap_dtls.c new file mode 100644 index 0000000..5cc6c1c --- /dev/null +++ b/src/common/capwap_dtls.c @@ -0,0 +1,785 @@ +#include "capwap.h" +#include "capwap_dtls.h" +#include "capwap_protocol.h" + +#include +#include +#include +#include + +#define CAPWAP_DTLS_CERT_VERIFY_DEPTH 1 +#define CAPWAP_DTLS_MTU_SIZE 16384 + +/* */ +static int capwap_bio_method_new(BIO* bio); +static int capwap_bio_method_free(BIO* bio); +static int capwap_bio_method_puts(BIO* bio, const char* str); +static int capwap_bio_method_read(BIO* bio, char* str, int length); +static int capwap_bio_method_write(BIO* bio, const char* str, int length); +static long capwap_bio_method_ctrl(BIO* bio, int cmd, long num, void* ptr); + +/* OpenSSL BIO methods */ +static BIO_METHOD bio_methods_memory = { + BIO_TYPE_DGRAM, + "dtls capwap packet", + capwap_bio_method_write, + capwap_bio_method_read, + capwap_bio_method_puts, + NULL, + capwap_bio_method_ctrl, + capwap_bio_method_new, + capwap_bio_method_free, + NULL, +}; + +/* OpenSSL BIO custom data */ +struct bio_capwap_data { + int mtu; + struct sockaddr_storage peer; + struct capwap_dtls* dtls; + capwap_bio_send send; + void* param; +}; + +/* */ +static BIO* capwap_bio_new() { + BIO* result; + + result = BIO_new(&bio_methods_memory); + if (result) { + memset(result->ptr, 0, sizeof(struct bio_capwap_data)); + } + + return result; +} + +/* */ +static int capwap_bio_method_new(BIO* bio) { + bio->init = 1; + bio->num = 0; + bio->flags = 0; + bio->ptr = (char*)capwap_alloc(sizeof(struct bio_capwap_data)); + + return 1; +} + +/* */ +static int capwap_bio_method_free(BIO* bio) { + if (bio == NULL) { + return 0; + } else if (bio->ptr) { + capwap_free(bio->ptr); + } + + return 1; +} + +/* */ +static int capwap_bio_method_puts(BIO* bio, const char* str) { + return capwap_bio_method_write(bio, str, strlen(str)); +} + +/* */ +static int capwap_bio_method_read(BIO* bio, char* str, int length) { + struct bio_capwap_data* data = (struct bio_capwap_data*)bio->ptr; + struct capwap_dtls_header* dtlspreamble; + int size; + + /* Check read packet */ + if ((data->dtls->length < sizeof(struct capwap_dtls_header)) || !data->dtls->buffer) { + if (!data->dtls->length && !data->dtls->buffer) { + BIO_set_retry_read(bio); /* Notify empty buffer */ + } + + return -1; + } + + /* Check DTLS Capwap Preamble */ + dtlspreamble = (struct capwap_dtls_header*)data->dtls->buffer; + if ((dtlspreamble->preamble.version != CAPWAP_PROTOCOL_VERSION) || (dtlspreamble->preamble.type != CAPWAP_PREAMBLE_DTLS_HEADER)) { + capwap_logging_debug("Wrong DTLS Capwap Preamble"); + return -1; /* Wrong DTLS Capwap Preamble */ + } + + /* */ + size = data->dtls->length - sizeof(struct capwap_dtls_header); + data->dtls->length = 0; + + data->dtls->buffer += sizeof(struct capwap_dtls_header); + if (size > length) { + data->dtls->buffer = NULL; + return -1; + } + + /* Copy DTLS packet */ + memcpy(str, data->dtls->buffer, size); + data->dtls->buffer = NULL; + + return size; +} + +/* */ +static int capwap_bio_method_write(BIO* bio, const char* str, int length) { + struct bio_capwap_data* data = (struct bio_capwap_data*)bio->ptr; + char buffer[CAPWAP_MAX_PACKET_SIZE]; + struct capwap_dtls_header* dtlspreamble = (struct capwap_dtls_header*)&buffer[0]; + + /* Check for maxium size of packet */ + if (length > (CAPWAP_MAX_PACKET_SIZE - sizeof(struct capwap_dtls_header))) { + return -1; + } + + /* Create DTLS Capwap Preamble */ + dtlspreamble->preamble.version = CAPWAP_PROTOCOL_VERSION; + dtlspreamble->preamble.type = CAPWAP_PREAMBLE_DTLS_HEADER; + dtlspreamble->reserved1 = dtlspreamble->reserved2 = dtlspreamble->reserved3 = 0; + memcpy(&buffer[0] + sizeof(struct capwap_dtls_header), str, length); + + /* Send packet */ + if (!data->send(data->dtls, buffer, length + sizeof(struct capwap_dtls_header), data->param)) { + return -1; + } + + /* Don't return size of DTLS Capwap Preamble */ + return length; +} + +/* */ +static long capwap_bio_method_ctrl(BIO* bio, int cmd, long num, void* ptr) { + long result = 1; + struct bio_capwap_data* data = (struct bio_capwap_data*)bio->ptr; + + switch (cmd) { + case BIO_CTRL_RESET: { + result = 0; + break; + } + + case BIO_CTRL_EOF: { + result = 0; + break; + } + + case BIO_CTRL_INFO: { + result = 0; + break; + } + + case BIO_CTRL_GET_CLOSE: { + result = bio->shutdown; + break; + } + + case BIO_CTRL_SET_CLOSE: { + bio->shutdown = (int)num; + break; + } + + case BIO_CTRL_WPENDING: + case BIO_CTRL_PENDING: { + result = 0; + break; + } + + case BIO_CTRL_DUP: + case BIO_CTRL_FLUSH: { + result = 1; + break; + } + + case BIO_CTRL_PUSH: { + result = 0; + break; + } + + case BIO_CTRL_POP: { + result = 0; + break; + } + + case BIO_CTRL_DGRAM_QUERY_MTU: { + data->mtu = CAPWAP_DTLS_MTU_SIZE; + result = data->mtu; + break; + } + + case BIO_CTRL_DGRAM_GET_MTU: { + result = data->mtu; + break; + } + + case BIO_CTRL_DGRAM_SET_MTU: { + data->mtu = (int)num; + result = data->mtu; + break; + } + + case BIO_CTRL_DGRAM_SET_PEER: { + memcpy(&data->peer, ptr, sizeof(struct sockaddr_storage)); + break; + } + + case BIO_CTRL_DGRAM_GET_PEER: { + memcpy(ptr, &data->peer, sizeof(struct sockaddr_storage)); + break; + } + + case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT: { + break; + } + + default: { + result = 0; + break; + } + } + + return result; +} + +/* */ +int capwap_crypt_init() { + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + + return 1; +} + +/* */ +void capwap_crypt_free() { + ERR_remove_state(0); + ERR_free_strings(); + + ENGINE_cleanup(); + EVP_cleanup(); + + CONF_modules_finish(); + CONF_modules_free(); + CONF_modules_unload(1); + + CRYPTO_cleanup_all_ex_data(); + sk_SSL_COMP_free (SSL_COMP_get_compression_methods()); +} + +/* */ +static int check_passwd(char* buffer, int size, int rwflag, void* userdata) { + int length; + struct capwap_dtls_context* dtlscontext = (struct capwap_dtls_context*)userdata; + + ASSERT(dtlscontext != NULL); + ASSERT(dtlscontext->mode == CAPWAP_DTLS_MODE_CERTIFICATE); + ASSERT(dtlscontext->cert.pwdprivatekey != NULL); + + length = strlen(dtlscontext->cert.pwdprivatekey); + if (!buffer || (size < (length + 1))) { + return 0; + } + + strcpy(buffer, dtlscontext->cert.pwdprivatekey); + return length; +} + +/* */ +static int verify_certificate(int ok, X509_STORE_CTX* ctx) { + int err; + int depth; + X509* err_cert; + char buf[256]; + int preverify_ok = 1; + + err_cert = X509_STORE_CTX_get_current_cert(ctx); + + err = X509_STORE_CTX_get_error(ctx); + X509_verify_cert_error_string(err); + + depth = X509_STORE_CTX_get_error_depth(ctx); + + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); + + if (depth > CAPWAP_DTLS_CERT_VERIFY_DEPTH) { + preverify_ok = 0; + err = X509_V_ERR_CERT_CHAIN_TOO_LONG; + X509_STORE_CTX_set_error(ctx, err); + } + + if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT)) { + X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256); + } + + return preverify_ok; +} + +static int create_cookie(SSL* ssl, unsigned char* cookie, unsigned int* cookie_len) { + int length; + unsigned char* buffer; + struct sockaddr_storage peer; + struct capwap_app_data* appdata; + + /* */ + appdata = (struct capwap_app_data*)SSL_get_app_data(ssl); + if (!appdata) { + return 0; + } + + /* Read peer information */ + BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer); + + /* Create buffer with peer's address and port */ + if (peer.ss_family == AF_INET) { + length = sizeof(struct in_addr) + sizeof(in_port_t); + } else if (peer.ss_family == AF_INET6) { + length = sizeof(struct in6_addr) + sizeof(in_port_t); + } else { + return 0; + } + + /* */ + buffer = capwap_alloc(length); + if (!buffer) { + capwap_outofmemory(); + } + + if (peer.ss_family == AF_INET) { + struct sockaddr_in* peeripv4 = (struct sockaddr_in*)&peer; + + memcpy(buffer, &peeripv4->sin_port, sizeof(in_port_t)); + memcpy(buffer + sizeof(in_port_t), &peeripv4->sin_addr, sizeof(struct in_addr)); + } else if (peer.ss_family == AF_INET6) { + struct sockaddr_in6* peeripv6 = (struct sockaddr_in6*)&peer; + + memcpy(buffer, &peeripv6->sin6_port, sizeof(in_port_t)); + memcpy(buffer + sizeof(in_port_t), &peeripv6->sin6_addr, sizeof(struct in6_addr)); + } + + /* Calculate HMAC of buffer using the secret */ + HMAC(EVP_sha1(), appdata->cookie, CAPWAP_COOKIE_SECRET_LENGTH, buffer, length, cookie, cookie_len); + capwap_free(buffer); + + return 1; +} + +/* */ +static int generate_cookie(SSL* ssl, unsigned char* cookie, unsigned int* cookie_len) { + unsigned int resultlength; + unsigned char result[EVP_MAX_MD_SIZE]; + + if (!create_cookie(ssl, &result[0], &resultlength)) { + return 0; + } + + /* Cookie generated */ + memcpy(cookie, result, resultlength); + *cookie_len = resultlength; + + return 1; +} + +/* */ +static int verify_cookie(SSL* ssl, unsigned char* cookie, unsigned int cookie_len) { + unsigned int resultlength; + unsigned char result[EVP_MAX_MD_SIZE]; + + if (!create_cookie(ssl, &result[0], &resultlength)) { + return 0; + } + + /* Check cookie */ + if ((cookie_len != resultlength) || (memcmp(result, cookie, resultlength) != 0)) { + return 0; + } + + return 1; +} + +/* */ +int capwap_crypt_createcontext(struct capwap_dtls_context* dtlscontext, struct capwap_dtls_param* param) { + int length; + + ASSERT(dtlscontext != NULL); + ASSERT(param != NULL); + + memset(dtlscontext, 0, sizeof(struct capwap_dtls_context)); + dtlscontext->type = param->type; + dtlscontext->mode = param->mode; + + /* Alloc context */ + dtlscontext->sslcontext = SSL_CTX_new(((param->type == CAPWAP_DTLS_SERVER) ? DTLSv1_server_method() : DTLSv1_client_method())); + if (!dtlscontext->sslcontext) { + capwap_logging_debug("Error to initialize dtls context"); + return 0; + } + + if (dtlscontext->mode == CAPWAP_DTLS_MODE_CERTIFICATE) { + /* Check context */ + if (!param->cert.filecert || !strlen(param->cert.filecert)) { + capwap_logging_debug("Error, request certificate file"); + capwap_crypt_freecontext(dtlscontext); + return 0; + } else if (!param->cert.filekey || !strlen(param->cert.filekey)) { + capwap_logging_debug("Error, request privatekey file"); + capwap_crypt_freecontext(dtlscontext); + return 0; + } else if (!param->cert.fileca || !strlen(param->cert.fileca)) { + capwap_logging_debug("Error, request ca file"); + capwap_crypt_freecontext(dtlscontext); + return 0; + } + + /* Public certificate */ + if (!SSL_CTX_use_certificate_file(dtlscontext->sslcontext, param->cert.filecert, SSL_FILETYPE_PEM)) { + capwap_logging_debug("Error to load certificate file"); + capwap_crypt_freecontext(dtlscontext); + return 0; + } + + /* Passwork decrypt privatekey */ + length = (param->cert.pwdprivatekey ? strlen(param->cert.pwdprivatekey) : 0); + dtlscontext->cert.pwdprivatekey = (char*)capwap_alloc(sizeof(char) * (length + 1)); + if (length > 0) { + strcpy(dtlscontext->cert.pwdprivatekey, param->cert.pwdprivatekey); + } + dtlscontext->cert.pwdprivatekey[length] = 0; + + SSL_CTX_set_default_passwd_cb(dtlscontext->sslcontext, check_passwd); + SSL_CTX_set_default_passwd_cb_userdata(dtlscontext->sslcontext, dtlscontext); + + /* Private key */ + if (!SSL_CTX_use_PrivateKey_file(dtlscontext->sslcontext, param->cert.filekey, SSL_FILETYPE_PEM)) { + capwap_logging_debug("Error to load private key file"); + capwap_crypt_freecontext(dtlscontext); + return 0; + } + + if (!SSL_CTX_check_private_key(dtlscontext->sslcontext)) { + capwap_logging_debug("Error to check private key"); + capwap_crypt_freecontext(dtlscontext); + return 0; + } + + /* Certificate Authority */ + if (!SSL_CTX_load_verify_locations(dtlscontext->sslcontext, param->cert.fileca, NULL)) { + capwap_logging_debug("Error to load ca file"); + capwap_crypt_freecontext(dtlscontext); + return 0; + } + + if (!SSL_CTX_set_default_verify_paths(dtlscontext->sslcontext)) { + capwap_crypt_freecontext(dtlscontext); + return 0; + } + + /* Verify certificate callback */ + SSL_CTX_set_verify(dtlscontext->sslcontext, ((param->type == CAPWAP_DTLS_SERVER) ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_PEER), verify_certificate); + + /* Cipher list: + TLS_RSA_WITH_AES_128_CBC_SHA + TLS_DHE_RSA_WITH_AES_128_CBC_SHA + TLS_RSA_WITH_AES_256_CBC_SHA + TLS_DHE_RSA_WITH_AES_256_CBC_SHA + */ + if (!SSL_CTX_set_cipher_list(dtlscontext->sslcontext, "AES128-SHA:DHE-RSA-AES128-SHA:AES256-SHA:DHE-RSA-AES256-SHA")) { + capwap_logging_debug("Error to select cipher list"); + capwap_crypt_freecontext(dtlscontext); + return 0; + } + } else if (dtlscontext->mode == CAPWAP_DTLS_MODE_PRESHAREDKEY) { + /* TODO */ + } else { + capwap_logging_debug("Invalid DTLS mode"); + capwap_crypt_freecontext(dtlscontext); + return 0; + } + + /* Cookie callback */ + RAND_bytes(dtlscontext->cookie, CAPWAP_COOKIE_SECRET_LENGTH); + SSL_CTX_set_cookie_generate_cb(dtlscontext->sslcontext, generate_cookie); + SSL_CTX_set_cookie_verify_cb(dtlscontext->sslcontext, verify_cookie); + + /* */ + SSL_CTX_set_read_ahead(dtlscontext->sslcontext, 1); + return 1; +} + +/* */ +void capwap_crypt_freecontext(struct capwap_dtls_context* dtlscontext) { + ASSERT(dtlscontext != NULL); + + /* */ + if (dtlscontext->mode == CAPWAP_DTLS_MODE_CERTIFICATE) { + if (dtlscontext->cert.pwdprivatekey) { + capwap_free(dtlscontext->cert.pwdprivatekey); + } + } + + /* Free context */ + if (dtlscontext->sslcontext) { + SSL_CTX_free(dtlscontext->sslcontext); + } + + memset(dtlscontext, 0, sizeof(struct capwap_dtls)); +} + +/* */ +int capwap_crypt_createsession(struct capwap_dtls* dtls, int sessiontype, struct capwap_dtls_context* dtlscontext, capwap_bio_send biosend, void* param) { + BIO* bio; + struct capwap_app_data* appdata; + + ASSERT(dtls != NULL); + ASSERT(dtlscontext != NULL); + ASSERT(biosend != NULL); + + memset(dtls, 0, sizeof(struct capwap_dtls)); + + /* Create ssl session */ + dtls->sslsession = SSL_new(dtlscontext->sslcontext); + if (!dtls->sslsession) { + capwap_logging_debug("Error to initialize dtls session"); + return 0; + } + + /* Create BIO */ + bio = capwap_bio_new(); + if (!bio) { + capwap_logging_debug("Error to initialize bio"); + capwap_crypt_free(dtls); + return 0; + } else { + struct bio_capwap_data* data = (struct bio_capwap_data*)bio->ptr; + data->dtls = dtls; + data->send = biosend; + data->param = param; + } + + /* Configure BIO */ + SSL_set_bio(dtls->sslsession, bio, bio); + + /* In server mode enable cookie exchange */ + if (dtlscontext->type == CAPWAP_DTLS_SERVER) { + SSL_set_options(dtls->sslsession, SSL_OP_COOKIE_EXCHANGE); + } + + /* Set static MTU size */ + SSL_set_options(dtls->sslsession, SSL_OP_NO_QUERY_MTU); + SSL_set_mtu(dtls->sslsession, CAPWAP_DTLS_MTU_SIZE); + + /* */ + SSL_set_verify_depth(dtls->sslsession, CAPWAP_DTLS_CERT_VERIFY_DEPTH + 1); + + /* */ + SSL_set_read_ahead(dtls->sslsession, 1); + + if (dtlscontext->type == CAPWAP_DTLS_SERVER) { + SSL_set_accept_state(dtls->sslsession); + } else { + SSL_set_connect_state(dtls->sslsession); + } + + /* SSL session app data */ + appdata = (struct capwap_app_data*)capwap_alloc(sizeof(struct capwap_app_data)); + if (!appdata) { + capwap_outofmemory(); + } + + appdata->cookie = &dtlscontext->cookie[0]; + SSL_set_app_data(dtls->sslsession, (void*)appdata); + + /* */ + dtls->action = CAPWAP_DTLS_ACTION_NONE; + dtls->session = sessiontype; + dtls->enable = 1; + + return 1; +} + +/* */ +static int capwap_crypt_handshake(struct capwap_dtls* dtls) { + int result; + + ASSERT(dtls != NULL); + ASSERT(dtls->enable != 0); + ASSERT((dtls->action == CAPWAP_DTLS_ACTION_NONE) || (dtls->action == CAPWAP_DTLS_ACTION_HANDSHAKE)); + + ERR_clear_error(); + result = SSL_do_handshake(dtls->sslsession); + if (result <= 0) { + result = SSL_get_error(dtls->sslsession, result); + if ((result == SSL_ERROR_WANT_READ) || (result == SSL_ERROR_WANT_WRITE)) { + /* Incomplete handshake */ + dtls->action = CAPWAP_DTLS_ACTION_HANDSHAKE; + return CAPWAP_HANDSHAKE_CONTINUE; + } + + /* Handshake error */ + dtls->action = CAPWAP_DTLS_ACTION_ERROR; + return CAPWAP_HANDSHAKE_ERROR; + } + + /* Check certificate */ + result = SSL_get_verify_result(dtls->sslsession); + if (result != X509_V_OK) { + dtls->action = CAPWAP_DTLS_ACTION_ERROR; + return CAPWAP_HANDSHAKE_ERROR; + } + + /* Handshake complete */ + dtls->action = CAPWAP_DTLS_ACTION_DATA; + return CAPWAP_HANDSHAKE_COMPLETE; +} + +/* */ +int capwap_crypt_open(struct capwap_dtls* dtls, struct sockaddr_storage* peeraddr) { + BIO_dgram_set_peer(SSL_get_rbio(dtls->sslsession), peeraddr); + return capwap_crypt_handshake(dtls); +} + +/* */ +void capwap_crypt_close(struct capwap_dtls* dtls) { + ASSERT(dtls != NULL); + ASSERT(dtls->enable != 0); + + if ((dtls->action == CAPWAP_DTLS_ACTION_DATA) || (dtls->action == CAPWAP_DTLS_ACTION_SHUTDOWN)) { + SSL_shutdown(dtls->sslsession); + } +} + +/* Change bio send */ +void capwap_crypt_change_bio_send(struct capwap_dtls* dtls, capwap_bio_send biosend, void* param) { + BIO* bio; + + ASSERT(dtls != NULL); + ASSERT(dtls->enable != 0); + ASSERT(biosend != NULL); + + bio = SSL_get_wbio(dtls->sslsession); + if ((bio != NULL) && (bio->ptr != NULL)) { + struct bio_capwap_data* data = (struct bio_capwap_data*)bio->ptr; + + data->send = biosend; + data->param = param; + } +} + +/* Change DTLS */ +void capwap_crypt_change_dtls(struct capwap_dtls* dtls, struct capwap_dtls* newdtls) { + BIO* bio; + + ASSERT(dtls != NULL); + ASSERT(dtls->enable != 0); + ASSERT(newdtls != NULL); + + memcpy(newdtls, dtls, sizeof(struct capwap_dtls)); + + /* Update DTLS into BIO */ + bio = SSL_get_rbio(dtls->sslsession); + if ((bio != NULL) && (bio->ptr != NULL)) { + struct bio_capwap_data* data = (struct bio_capwap_data*)bio->ptr; + + data->dtls = newdtls; + } +} + +/* */ +void capwap_crypt_freesession(struct capwap_dtls* dtls) { + ASSERT(dtls != NULL); + + /* Free SSL session */ + if (dtls->sslsession) { + struct capwap_app_data* appdata = (struct capwap_app_data*)SSL_get_app_data(dtls->sslsession); + if (appdata) { + capwap_free(appdata); + } + + SSL_free(dtls->sslsession); + } + + memset(dtls, 0, sizeof(struct capwap_dtls)); +} + +/* TODO: con SSL vengono utilizzati gli indirizzi predefiniti invece quelli specificati nella funzione. Reingegnerizzarla basandosi sul concetto di connessione */ +int capwap_crypt_sendto(struct capwap_dtls* dtls, int sock, void* buffer, int size, struct sockaddr_storage* sendfromaddr, struct sockaddr_storage* sendtoaddr) { + ASSERT(sock >= 0); + ASSERT(buffer != NULL); + ASSERT(size > 0); + ASSERT(sendtoaddr != NULL); + + if (!dtls || !dtls->enable) { + return capwap_sendto(sock, buffer, size, sendfromaddr, sendtoaddr); + } + + /* Valid DTLS status */ + if (dtls->action != CAPWAP_DTLS_ACTION_DATA) { + return 0; + } + + ERR_clear_error(); + return SSL_write(dtls->sslsession, buffer, size); +} + +/* */ +int capwap_decrypt_packet(struct capwap_dtls* dtls, void* encrybuffer, int size, void* plainbuffer, int maxsize) { + int sslerror; + int result = -1; + char* clone = NULL; + + ASSERT(dtls != NULL); + ASSERT(dtls->enable != 0); + ASSERT((dtls->action == CAPWAP_DTLS_ACTION_HANDSHAKE) || (dtls->action == CAPWAP_DTLS_ACTION_DATA)); + ASSERT(dtls->buffer == NULL); + ASSERT(dtls->length == 0); + ASSERT(encrybuffer != NULL); + ASSERT(size > 0); + ASSERT(maxsize > 0); + + /* */ + if (!plainbuffer) { + clone = capwap_clone(encrybuffer, size); + } + + dtls->buffer = (clone ? clone : encrybuffer); + dtls->length = size; + + /* */ + if (dtls->action == CAPWAP_DTLS_ACTION_HANDSHAKE) { + if (capwap_crypt_handshake(dtls) == CAPWAP_HANDSHAKE_ERROR) { + capwap_logging_debug("Error in DTLS handshake"); + result = CAPWAP_ERROR_CLOSE; /* Error handshake */ + } else { + result = CAPWAP_ERROR_AGAIN; /* Don't parsing DTLS packet */ + } + } else if (dtls->action == CAPWAP_DTLS_ACTION_DATA) { + ERR_clear_error(); + result = SSL_read(dtls->sslsession, (plainbuffer ? plainbuffer : encrybuffer), maxsize); + if (!result) { + int shutdown; + + /* Check shutdown status */ + shutdown = SSL_get_shutdown(dtls->sslsession); + if (shutdown & SSL_RECEIVED_SHUTDOWN) { + dtls->action = CAPWAP_DTLS_ACTION_SHUTDOWN; + result = CAPWAP_ERROR_SHUTDOWN; + } else { + result = CAPWAP_ERROR_AGAIN; + } + } else if (result < 0) { + /* Check error */ + sslerror = SSL_get_error(dtls->sslsession, result); + if ((sslerror == SSL_ERROR_WANT_READ) || (sslerror == SSL_ERROR_WANT_WRITE)) { + result = CAPWAP_ERROR_AGAIN; /* DTLS Renegotiation */ + } else { + result = CAPWAP_ERROR_CLOSE; + } + } + } + + /* Verify BIO read */ + ASSERT(dtls->buffer == NULL); + ASSERT(dtls->length == 0); + + /* Free clone */ + if (clone) { + capwap_free(clone); + } + + return result; +} diff --git a/src/common/capwap_dtls.h b/src/common/capwap_dtls.h new file mode 100644 index 0000000..795cade --- /dev/null +++ b/src/common/capwap_dtls.h @@ -0,0 +1,108 @@ +#ifndef __CAPWAP_DTLS_HEADER__ +#define __CAPWAP_DTLS_HEADER__ + +#include + +#define CAPWAP_DTLS_CLIENT 0 +#define CAPWAP_DTLS_SERVER 1 + +#define CAPWAP_DTLS_MODE_NONE 0 +#define CAPWAP_DTLS_MODE_CERTIFICATE 1 +#define CAPWAP_DTLS_MODE_PRESHAREDKEY 2 + +#define CAPWAP_DTLS_ACTION_NONE 0 +#define CAPWAP_DTLS_ACTION_HANDSHAKE 1 +#define CAPWAP_DTLS_ACTION_DATA 2 +#define CAPWAP_DTLS_ACTION_SHUTDOWN 3 +#define CAPWAP_DTLS_ACTION_ERROR 4 + +#define CAPWAP_HANDSHAKE_ERROR -1 +#define CAPWAP_HANDSHAKE_CONTINUE 0 +#define CAPWAP_HANDSHAKE_COMPLETE 1 + +#define CAPWAP_DTLS_CONTROL_SESSION 0 +#define CAPWAP_DTLS_DATA_SESSION 1 + +#define CAPWAP_COOKIE_SECRET_LENGTH 16 + +#define CAPWAP_ERROR_AGAIN 0 +#define CAPWAP_ERROR_SHUTDOWN -1 +#define CAPWAP_ERROR_CLOSE -2 + +struct capwap_dtls_param { + int type; + int mode; + + union { + struct { + int dummy; /* TODO */ + } presharedkey; + + struct { + /* Certificate files */ + char* filecert; + char* filekey; + char* fileca; + + /* Password for private key */ + char* pwdprivatekey; + } cert; + }; +}; + +struct capwap_dtls_context { + int type; + int mode; + + SSL_CTX* sslcontext; + + /* Cookie */ + unsigned char cookie[CAPWAP_COOKIE_SECRET_LENGTH]; + + union { + struct { + int dummy; /* TODO */ + } presharedkey; + + struct { + char* pwdprivatekey; /* Password for private key */ + } cert; + }; +}; + +struct capwap_dtls { + int enable; + int action; + int session; + + SSL* sslsession; + + /* Buffer read */ + void* buffer; + int length; +}; + +struct capwap_app_data { + unsigned char* cookie; +}; + +typedef int(*capwap_bio_send)(struct capwap_dtls* dtls, char* buffer, int length, void* param); + +int capwap_crypt_init(); +void capwap_crypt_free(); + +int capwap_crypt_createcontext(struct capwap_dtls_context* dtlscontext, struct capwap_dtls_param* param); +void capwap_crypt_freecontext(struct capwap_dtls_context* dtlscontext); + +int capwap_crypt_createsession(struct capwap_dtls* dtls, int sessiontype, struct capwap_dtls_context* dtlscontext, capwap_bio_send biosend, void* param); +void capwap_crypt_freesession(struct capwap_dtls* dtls); + +int capwap_crypt_open(struct capwap_dtls* dtls, struct sockaddr_storage* peeraddr); +void capwap_crypt_close(struct capwap_dtls* dtls); +void capwap_crypt_change_bio_send(struct capwap_dtls* dtls, capwap_bio_send biosend, void* param); +void capwap_crypt_change_dtls(struct capwap_dtls* dtls, struct capwap_dtls* newdtls); + +int capwap_crypt_sendto(struct capwap_dtls* dtls, int sock, void* buffer, int size, struct sockaddr_storage* sendfromaddr, struct sockaddr_storage* sendtoaddr); +int capwap_decrypt_packet(struct capwap_dtls* dtls, void* encrybuffer, int size, void* plainbuffer, int maxsize); + +#endif /* __CAPWAP_DTLS_HEADER__ */ diff --git a/src/common/capwap_element.c b/src/common/capwap_element.c new file mode 100644 index 0000000..568073a --- /dev/null +++ b/src/common/capwap_element.c @@ -0,0 +1,1373 @@ +#include "capwap.h" +#include "capwap_element.h" +#include "capwap_protocol.h" +#include "capwap_array.h" + +static struct capwap_message_elements_func standard_message_elements[CAPWAP_MESSAGE_ELEMENTS_COUNT] = { + /* CAPWAP_ELEMENT_ACDESCRIPTION */ { capwap_acdescriptor_element_create, capwap_acdescriptor_element_validate, capwap_acdescriptor_element_parsing, capwap_acdescriptor_element_free }, + /* CAPWAP_ELEMENT_ACIPV4LIST */ { capwap_acipv4list_element_create, capwap_acipv4list_element_validate, capwap_acipv4list_element_parsing, capwap_acipv4list_element_free }, + /* CAPWAP_ELEMENT_ACIPV6LIST */ { capwap_acipv6list_element_create, capwap_acipv6list_element_validate, capwap_acipv6list_element_parsing, capwap_acipv6list_element_free }, + /* CAPWAP_ELEMENT_ACNAME */ { capwap_acname_element_create, capwap_acname_element_validate, capwap_acname_element_parsing, capwap_acname_element_free }, + /* CAPWAP_ELEMENT_ACNAMEPRIORITY */ { capwap_acnamepriority_element_create, capwap_acnamepriority_element_validate, capwap_acnamepriority_element_parsing, capwap_acnamepriority_element_free }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* Reserved */ { NULL, NULL }, + /* CAPWAP_ELEMENT_CONTROLIPV4 */ { capwap_controlipv4_element_create, capwap_controlipv4_element_validate, capwap_controlipv4_element_parsing, capwap_controlipv4_element_free }, + /* CAPWAP_ELEMENT_CONTROLIPV6 */ { capwap_controlipv6_element_create, capwap_controlipv6_element_validate, capwap_controlipv6_element_parsing, capwap_controlipv6_element_free }, + /* CAPWAP_ELEMENT_TIMERS */ { capwap_timers_element_create, capwap_timers_element_validate, capwap_timers_element_parsing, capwap_timers_element_free }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* CAPWAP_ELEMENT_DECRYPTERRORREPORTPERIOD */ { capwap_decrypterrorreportperiod_element_create, capwap_decrypterrorreportperiod_element_validate, capwap_decrypterrorreportperiod_element_parsing, capwap_decrypterrorreportperiod_element_free }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* Reserved */ { NULL, NULL }, + /* CAPWAP_ELEMENT_DISCOVERYTYPE */ { capwap_discoverytype_element_create, capwap_discoverytype_element_validate, capwap_discoverytype_element_parsing, capwap_discoverytype_element_free }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* CAPWAP_ELEMENT_IDLETIMEOUT */ { capwap_idletimeout_element_create, capwap_idletimeout_element_validate, capwap_idletimeout_element_parsing, capwap_idletimeout_element_free }, + /* */ { NULL, NULL }, + /* CAPWAP_ELEMENT_IMAGEIDENTIFIER */ { capwap_imageidentifier_element_create, capwap_imageidentifier_element_validate, capwap_imageidentifier_element_parsing, capwap_imageidentifier_element_free }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* CAPWAP_ELEMENT_LOCATION */ { capwap_location_element_create, capwap_location_element_validate, capwap_location_element_parsing, capwap_location_element_free }, + /* CAPWAP_ELEMENT_MAXIMUMLENGTH */ { capwap_maximumlength_element_create, capwap_maximumlength_element_validate, capwap_maximumlength_element_parsing, capwap_maximumlength_element_free }, + /* CAPWAP_ELEMENT_LOCALIPV4 */ { capwap_localipv4_element_create, capwap_localipv4_element_validate, capwap_localipv4_element_parsing, capwap_localipv4_element_free }, + /* CAPWAP_ELEMENT_RADIOADMSTATE */ { capwap_radioadmstate_element_create, capwap_radioadmstate_element_validate, capwap_radioadmstate_element_parsing, capwap_radioadmstate_element_free }, + /* CAPWAP_ELEMENT_RADIOOPRSTATE */ { capwap_radiooprstate_element_create, capwap_radiooprstate_element_validate, capwap_radiooprstate_element_parsing, capwap_radiooprstate_element_free }, + /* CAPWAP_ELEMENT_RESULTCODE */ { capwap_resultcode_element_create, capwap_resultcode_element_validate, capwap_resultcode_element_parsing, capwap_resultcode_element_free }, + /* CAPWAP_ELEMENT_RETURNEDMESSAGE */ { capwap_returnedmessage_element_create, capwap_returnedmessage_element_validate, capwap_returnedmessage_element_parsing, capwap_returnedmessage_element_free }, + /* CAPWAP_ELEMENT_SESSIONID */ { capwap_sessionid_element_create, capwap_sessionid_element_validate, capwap_sessionid_element_parsing, capwap_sessionid_element_free }, + /* CAPWAP_ELEMENT_STATISTICSTIMER */ { capwap_statisticstimer_element_create, capwap_statisticstimer_element_validate, capwap_statisticstimer_element_parsing, capwap_statisticstimer_element_free }, + /* CAPWAP_ELEMENT_VENDORPAYLOAD */ { capwap_vendorpayload_element_create, capwap_vendorpayload_element_validate, capwap_vendorpayload_element_parsing, capwap_vendorpayload_element_free }, + /* CAPWAP_ELEMENT_WTPBOARDDATA */ { capwap_wtpboarddata_element_create, capwap_wtpboarddata_element_validate, capwap_wtpboarddata_element_parsing, capwap_wtpboarddata_element_free }, + /* CAPWAP_ELEMENT_WTPDESCRIPTOR */ { capwap_wtpdescriptor_element_create, capwap_wtpdescriptor_element_validate, capwap_wtpdescriptor_element_parsing, capwap_wtpdescriptor_element_free }, + /* CAPWAP_ELEMENT_WTPFALLBACK */ { capwap_wtpfallback_element_create, capwap_wtpfallback_element_validate, capwap_wtpfallback_element_parsing, capwap_wtpfallback_element_free }, + /* CAPWAP_ELEMENT_WTPFRAMETUNNELMODE */ { capwap_wtpframetunnelmode_element_create, capwap_wtpframetunnelmode_element_validate, capwap_wtpframetunnelmode_element_parsing, capwap_wtpframetunnelmode_element_free }, + /* Reserved */ { NULL, NULL }, + /* Reserved */ { NULL, NULL }, + /* CAPWAP_ELEMENT_WTPMACTYPE */ { capwap_wtpmactype_element_create, capwap_wtpmactype_element_validate, capwap_wtpmactype_element_parsing, capwap_wtpmactype_element_free }, + /* CAPWAP_ELEMENT_WTPNAME */ { capwap_wtpname_element_create, capwap_wtpname_element_validate, capwap_wtpname_element_parsing, capwap_wtpname_element_free }, + /* Reserved */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* CAPWAP_ELEMENT_WTPREBOOTSTAT */ { capwap_wtprebootstat_element_create, capwap_wtprebootstat_element_validate, capwap_wtprebootstat_element_parsing, capwap_wtprebootstat_element_free }, + /* CAPWAP_ELEMENT_WTPSTATICIPADDRESS */ { capwap_wtpstaticipaddress_element_create, capwap_wtpstaticipaddress_element_validate, capwap_wtpstaticipaddress_element_parsing, capwap_wtpstaticipaddress_element_free }, + /* CAPWAP_ELEMENT_LOCALIPV6 */ { capwap_localipv6_element_create, capwap_localipv6_element_validate, capwap_localipv6_element_parsing, capwap_localipv6_element_free }, + /* CAPWAP_ELEMENT_TRANSPORT */ { capwap_transport_element_create, capwap_transport_element_validate, capwap_transport_element_parsing, capwap_transport_element_free }, + /* CAPWAP_ELEMENT_MTUDISCOVERY */ { capwap_mtudiscovery_element_create, capwap_mtudiscovery_element_validate, capwap_mtudiscovery_element_parsing, capwap_mtudiscovery_element_free }, + /* CAPWAP_ELEMENT_ECNSUPPORT */ { capwap_ecnsupport_element_create, capwap_ecnsupport_element_validate, capwap_ecnsupport_element_parsing, capwap_ecnsupport_element_free }, +}; + +static struct capwap_message_elements_func ieee80211_message_elements[CAPWAP_80211_MESSAGE_ELEMENTS_COUNT] = { + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* */ { NULL, NULL }, + /* CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION */ { capwap_80211_wtpradioinformation_element_create, capwap_80211_wtpradioinformation_element_validate, capwap_80211_wtpradioinformation_element_parsing, capwap_80211_wtpradioinformation_element_free }, +}; + +/* */ +struct capwap_message_elements_func* capwap_get_message_element(unsigned long code) { + if ((code >= CAPWAP_MESSAGE_ELEMENTS_START) && (code <= CAPWAP_MESSAGE_ELEMENTS_STOP)) { + return &standard_message_elements[code - CAPWAP_MESSAGE_ELEMENTS_START]; + } else if ((code >= CAPWAP_80211_MESSAGE_ELEMENTS_START) && (code <= CAPWAP_80211_MESSAGE_ELEMENTS_STOP)) { + return &ieee80211_message_elements[code - CAPWAP_80211_MESSAGE_ELEMENTS_START]; + } + + return NULL; +} + +/* */ +void capwap_init_element_discovery_request(struct capwap_element_discovery_request* element, unsigned short binding) { + ASSERT(element != NULL); + + memset(element, 0, sizeof(struct capwap_element_discovery_request)); + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + element->binding.ieee80211.wtpradioinformation = capwap_array_create(sizeof(struct capwap_80211_wtpradioinformation_element*), 0); + } +} + +/* */ +int capwap_parsing_element_discovery_request(struct capwap_element_discovery_request* element, struct capwap_list_item* item) { + ASSERT(element != NULL); + + while (item != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_DISCOVERYTYPE: { + element->discoverytype = (struct capwap_discoverytype_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPBOARDDATA: { + element->wtpboarddata = (struct capwap_wtpboarddata_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPDESCRIPTOR: { + element->wtpdescriptor = (struct capwap_wtpdescriptor_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPFRAMETUNNELMODE: { + element->wtpframetunnel = (struct capwap_wtpframetunnelmode_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPMACTYPE: { + element->wtpmactype = (struct capwap_wtpmactype_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_MTUDISCOVERY: { + element->mtudiscovery = (struct capwap_mtudiscovery_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_VENDORPAYLOAD: { + element->vendorpayload = (struct capwap_vendorpayload_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION: { + struct capwap_80211_wtpradioinformation_element** radio; + + radio = (struct capwap_80211_wtpradioinformation_element**)capwap_array_get_item_pointer(element->binding.ieee80211.wtpradioinformation, element->binding.ieee80211.wtpradioinformation->count); + *radio = (struct capwap_80211_wtpradioinformation_element*)f->parsing(elementitem); + break; + } + } + + /* Next element */ + item = item->next; + } + + return 1; +} + +/* */ +void capwap_free_element_discovery_request(struct capwap_element_discovery_request* element, unsigned short binding) { + unsigned long i; + struct capwap_message_elements_func* f; + + ASSERT(element != NULL); + + if (element->discoverytype) { + capwap_get_message_element(CAPWAP_ELEMENT_DISCOVERYTYPE)->free(element->discoverytype); + } + + if (element->wtpboarddata) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPBOARDDATA)->free(element->wtpboarddata); + } + + if (element->wtpdescriptor) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPDESCRIPTOR)->free(element->wtpdescriptor); + } + + if (element->wtpframetunnel) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPFRAMETUNNELMODE)->free(element->wtpframetunnel); + } + + if (element->wtpmactype) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPMACTYPE)->free(element->wtpmactype); + } + + if (element->mtudiscovery) { + capwap_get_message_element(CAPWAP_ELEMENT_MTUDISCOVERY)->free(element->mtudiscovery); + } + + if (element->vendorpayload) { + capwap_get_message_element(CAPWAP_ELEMENT_VENDORPAYLOAD)->free(element->vendorpayload); + } + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + if (element->binding.ieee80211.wtpradioinformation->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION); + + for (i = 0; i < element->binding.ieee80211.wtpradioinformation->count; i++) { + f->free(*(struct capwap_80211_wtpradioinformation_element**)capwap_array_get_item_pointer(element->binding.ieee80211.wtpradioinformation, i)); + } + } + + capwap_array_free(element->binding.ieee80211.wtpradioinformation); + } + + /* Clean */ + memset(element, 0, sizeof(struct capwap_element_discovery_request)); +} + +/* */ +void capwap_init_element_discovery_response(struct capwap_element_discovery_response* element, unsigned short binding) { + ASSERT(element != NULL); + + memset(element, 0, sizeof(struct capwap_element_discovery_response)); + + element->controlipv4 = capwap_array_create(sizeof(struct capwap_controlipv4_element*), 0); + element->controlipv6 = capwap_array_create(sizeof(struct capwap_controlipv6_element*), 0); + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + element->binding.ieee80211.wtpradioinformation = capwap_array_create(sizeof(struct capwap_80211_wtpradioinformation_element*), 0); + } +} + +/* */ +int capwap_parsing_element_discovery_response(struct capwap_element_discovery_response* element, struct capwap_list_item* item) { + ASSERT(element != NULL); + + while (item != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_ACDESCRIPTION: { + element->acdescriptor = (struct capwap_acdescriptor_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_ACNAME: { + element->acname = (struct capwap_acname_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_CONTROLIPV4: { + struct capwap_controlipv4_element** controlipv4; + + controlipv4 = (struct capwap_controlipv4_element**)capwap_array_get_item_pointer(element->controlipv4, element->controlipv4->count); + *controlipv4 = (struct capwap_controlipv4_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_CONTROLIPV6: { + struct capwap_controlipv6_element** controlipv6; + + controlipv6 = (struct capwap_controlipv6_element**)capwap_array_get_item_pointer(element->controlipv6, element->controlipv6->count); + *controlipv6 = (struct capwap_controlipv6_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_VENDORPAYLOAD: { + element->vendorpayload = (struct capwap_vendorpayload_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION: { + struct capwap_80211_wtpradioinformation_element** radio; + + radio = (struct capwap_80211_wtpradioinformation_element**)capwap_array_get_item_pointer(element->binding.ieee80211.wtpradioinformation, element->binding.ieee80211.wtpradioinformation->count); + *radio = (struct capwap_80211_wtpradioinformation_element*)f->parsing(elementitem); + break; + } + } + + /* Next element */ + item = item->next; + } + + return 1; +} + +/* */ +void capwap_free_element_discovery_response(struct capwap_element_discovery_response* element, unsigned short binding) { + unsigned long i; + struct capwap_message_elements_func* f; + + ASSERT(element != NULL); + + if (element->acdescriptor) { + capwap_get_message_element(CAPWAP_ELEMENT_ACDESCRIPTION)->free(element->acdescriptor); + } + + if (element->acname) { + capwap_get_message_element(CAPWAP_ELEMENT_ACNAME)->free(element->acname); + } + + if (element->controlipv4->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_CONTROLIPV4); + + for (i = 0; i < element->controlipv4->count; i++) { + f->free(*(struct capwap_controlipv4_element**)capwap_array_get_item_pointer(element->controlipv4, i)); + } + } + capwap_array_free(element->controlipv4); + + if (element->controlipv6->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_CONTROLIPV6); + + for (i = 0; i < element->controlipv6->count; i++) { + f->free(*(struct capwap_controlipv6_element**)capwap_array_get_item_pointer(element->controlipv6, i)); + } + } + capwap_array_free(element->controlipv6); + + if (element->vendorpayload) { + capwap_get_message_element(CAPWAP_ELEMENT_VENDORPAYLOAD)->free(element->vendorpayload); + } + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + if (element->binding.ieee80211.wtpradioinformation->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION); + + for (i = 0; i < element->binding.ieee80211.wtpradioinformation->count; i++) { + f->free(*(struct capwap_80211_wtpradioinformation_element**)capwap_array_get_item_pointer(element->binding.ieee80211.wtpradioinformation, i)); + } + } + + capwap_array_free(element->binding.ieee80211.wtpradioinformation); + } + + /* Clean */ + memset(element, 0, sizeof(struct capwap_element_discovery_response)); +} + +/* */ +void capwap_init_element_join_request(struct capwap_element_join_request* element, unsigned short binding) { + ASSERT(element != NULL); + + memset(element, 0, sizeof(struct capwap_element_join_request)); + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + element->binding.ieee80211.wtpradioinformation = capwap_array_create(sizeof(struct capwap_80211_wtpradioinformation_element*), 0); + } +} + +/* */ +int capwap_parsing_element_join_request(struct capwap_element_join_request* element, struct capwap_list_item* item) { + ASSERT(element); + + while (item != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_LOCATION: { + element->locationdata = (struct capwap_location_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPBOARDDATA: { + element->wtpboarddata = (struct capwap_wtpboarddata_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPDESCRIPTOR: { + element->wtpdescriptor = (struct capwap_wtpdescriptor_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPNAME: { + element->wtpname = (struct capwap_wtpname_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_SESSIONID: { + element->sessionid = (struct capwap_sessionid_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPFRAMETUNNELMODE: { + element->wtpframetunnel = (struct capwap_wtpframetunnelmode_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPMACTYPE: { + element->wtpmactype = (struct capwap_wtpmactype_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_ECNSUPPORT: { + element->ecnsupport = (struct capwap_ecnsupport_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_LOCALIPV4: { + element->localipv4 = (struct capwap_localipv4_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_LOCALIPV6: { + element->localipv6 = (struct capwap_localipv6_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_TRANSPORT: { + element->trasport = (struct capwap_transport_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_MAXIMUMLENGTH: { + element->maxiumlength = (struct capwap_maximumlength_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPREBOOTSTAT: { + element->wtprebootstat = (struct capwap_wtprebootstat_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_VENDORPAYLOAD: { + element->vendorpayload = (struct capwap_vendorpayload_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION: { + struct capwap_80211_wtpradioinformation_element** radio; + + radio = (struct capwap_80211_wtpradioinformation_element**)capwap_array_get_item_pointer(element->binding.ieee80211.wtpradioinformation, element->binding.ieee80211.wtpradioinformation->count); + *radio = (struct capwap_80211_wtpradioinformation_element*)f->parsing(elementitem); + break; + } + } + + /* Next element */ + item = item->next; + } + + return 1; +} + +/* */ +void capwap_free_element_join_request(struct capwap_element_join_request* element, unsigned short binding) { + unsigned long i; + struct capwap_message_elements_func* f; + + ASSERT(element != NULL); + + if (element->locationdata) { + capwap_get_message_element(CAPWAP_ELEMENT_LOCATION)->free(element->locationdata); + } + + if (element->wtpboarddata) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPBOARDDATA)->free(element->wtpboarddata); + } + + if (element->wtpdescriptor) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPDESCRIPTOR)->free(element->wtpdescriptor); + } + + if (element->wtpname) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPNAME)->free(element->wtpname); + } + + if (element->sessionid) { + capwap_get_message_element(CAPWAP_ELEMENT_SESSIONID)->free(element->sessionid); + } + + if (element->wtpframetunnel) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPFRAMETUNNELMODE)->free(element->wtpframetunnel); + } + + if (element->wtpmactype) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPMACTYPE)->free(element->wtpmactype); + } + + if (element->ecnsupport) { + capwap_get_message_element(CAPWAP_ELEMENT_ECNSUPPORT)->free(element->ecnsupport); + } + + if (element->localipv4) { + capwap_get_message_element(CAPWAP_ELEMENT_LOCALIPV4)->free(element->localipv4); + } + + if (element->localipv6) { + capwap_get_message_element(CAPWAP_ELEMENT_LOCALIPV6)->free(element->localipv6); + } + + if (element->trasport) { + capwap_get_message_element(CAPWAP_ELEMENT_TRANSPORT)->free(element->trasport); + } + + if (element->maxiumlength) { + capwap_get_message_element(CAPWAP_ELEMENT_MAXIMUMLENGTH)->free(element->maxiumlength); + } + + if (element->wtprebootstat) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPREBOOTSTAT)->free(element->wtprebootstat); + } + + if (element->vendorpayload) { + capwap_get_message_element(CAPWAP_ELEMENT_VENDORPAYLOAD)->free(element->vendorpayload); + } + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + if (element->binding.ieee80211.wtpradioinformation->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION); + + for (i = 0; i < element->binding.ieee80211.wtpradioinformation->count; i++) { + f->free(*(struct capwap_80211_wtpradioinformation_element**)capwap_array_get_item_pointer(element->binding.ieee80211.wtpradioinformation, i)); + } + } + + capwap_array_free(element->binding.ieee80211.wtpradioinformation); + } + + /* Clean */ + memset(element, 0, sizeof(struct capwap_element_join_request)); +} + +/* */ +void capwap_init_element_join_response(struct capwap_element_join_response* element, unsigned short binding) { + ASSERT(element != NULL); + + memset(element, 0, sizeof(struct capwap_element_join_response)); + + element->returnedmessage = capwap_array_create(sizeof(struct capwap_returnedmessage_element*), 0); + element->controlipv4 = capwap_array_create(sizeof(struct capwap_controlipv4_element*), 0); + element->controlipv6 = capwap_array_create(sizeof(struct capwap_controlipv6_element*), 0); + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + element->binding.ieee80211.wtpradioinformation = capwap_array_create(sizeof(struct capwap_80211_wtpradioinformation_element*), 0); + } +} + +/* */ +int capwap_parsing_element_join_response(struct capwap_element_join_response* element, struct capwap_list_item* item) { + ASSERT(element != NULL); + + while (item != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_RESULTCODE: { + element->resultcode = (struct capwap_resultcode_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_RETURNEDMESSAGE: { + struct capwap_returnedmessage_element** returnedmessage; + + returnedmessage = (struct capwap_returnedmessage_element**)capwap_array_get_item_pointer(element->returnedmessage, element->returnedmessage->count); + *returnedmessage = (struct capwap_returnedmessage_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_ACDESCRIPTION: { + element->acdescriptor = (struct capwap_acdescriptor_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_ACNAME: { + element->acname = (struct capwap_acname_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_ECNSUPPORT: { + element->ecnsupport = (struct capwap_ecnsupport_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_CONTROLIPV4: { + struct capwap_controlipv4_element** controlipv4; + + controlipv4 = (struct capwap_controlipv4_element**)capwap_array_get_item_pointer(element->controlipv4, element->controlipv4->count); + *controlipv4 = (struct capwap_controlipv4_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_CONTROLIPV6: { + struct capwap_controlipv6_element** controlipv6; + + controlipv6 = (struct capwap_controlipv6_element**)capwap_array_get_item_pointer(element->controlipv6, element->controlipv6->count); + *controlipv6 = (struct capwap_controlipv6_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_LOCALIPV4: { + element->localipv4 = (struct capwap_localipv4_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_LOCALIPV6: { + element->localipv6 = (struct capwap_localipv6_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_ACIPV4LIST: { + element->acipv4list = (capwap_acipv4list_element_array*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_ACIPV6LIST: { + element->acipv6list = (capwap_acipv6list_element_array*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_TRANSPORT: { + element->trasport = (struct capwap_transport_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_IMAGEIDENTIFIER: { + element->imageidentifier = (struct capwap_imageidentifier_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_MAXIMUMLENGTH: { + element->maxiumlength = (struct capwap_maximumlength_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_VENDORPAYLOAD: { + element->vendorpayload = (struct capwap_vendorpayload_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION: { + struct capwap_80211_wtpradioinformation_element** radio; + + radio = (struct capwap_80211_wtpradioinformation_element**)capwap_array_get_item_pointer(element->binding.ieee80211.wtpradioinformation, element->binding.ieee80211.wtpradioinformation->count); + *radio = (struct capwap_80211_wtpradioinformation_element*)f->parsing(elementitem); + break; + } + } + + /* Next element */ + item = item->next; + } + + return 1; +} + +/* */ +void capwap_free_element_join_response(struct capwap_element_join_response* element, unsigned short binding) { + unsigned long i; + struct capwap_message_elements_func* f; + + ASSERT(element != NULL); + + if (element->resultcode) { + capwap_get_message_element(CAPWAP_ELEMENT_RESULTCODE)->free(element->resultcode); + } + + if (element->returnedmessage->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_RETURNEDMESSAGE); + + for (i = 0; i < element->returnedmessage->count; i++) { + f->free(*(struct capwap_returnedmessage_element**)capwap_array_get_item_pointer(element->returnedmessage, i)); + } + } + capwap_array_free(element->returnedmessage); + + if (element->acdescriptor) { + capwap_get_message_element(CAPWAP_ELEMENT_ACDESCRIPTION)->free(element->acdescriptor); + } + + if (element->acname) { + capwap_get_message_element(CAPWAP_ELEMENT_ACNAME)->free(element->acname); + } + + if (element->ecnsupport) { + capwap_get_message_element(CAPWAP_ELEMENT_ECNSUPPORT)->free(element->ecnsupport); + } + + if (element->controlipv4->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_CONTROLIPV4); + + for (i = 0; i < element->controlipv4->count; i++) { + f->free(*(struct capwap_controlipv4_element**)capwap_array_get_item_pointer(element->controlipv4, i)); + } + } + capwap_array_free(element->controlipv4); + + if (element->controlipv6->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_CONTROLIPV6); + + for (i = 0; i < element->controlipv6->count; i++) { + f->free(*(struct capwap_controlipv6_element**)capwap_array_get_item_pointer(element->controlipv6, i)); + } + } + capwap_array_free(element->controlipv6); + + if (element->localipv4) { + capwap_get_message_element(CAPWAP_ELEMENT_LOCALIPV4)->free(element->localipv4); + } + + if (element->localipv6) { + capwap_get_message_element(CAPWAP_ELEMENT_LOCALIPV6)->free(element->localipv6); + } + + if (element->acipv4list) { + capwap_get_message_element(CAPWAP_ELEMENT_ACIPV4LIST)->free(element->acipv4list); + } + + if (element->acipv6list) { + capwap_get_message_element(CAPWAP_ELEMENT_ACIPV6LIST)->free(element->acipv6list); + } + + if (element->trasport) { + capwap_get_message_element(CAPWAP_ELEMENT_TRANSPORT)->free(element->trasport); + } + + if (element->imageidentifier) { + capwap_get_message_element(CAPWAP_ELEMENT_IMAGEIDENTIFIER)->free(element->imageidentifier); + } + + if (element->maxiumlength) { + capwap_get_message_element(CAPWAP_ELEMENT_MAXIMUMLENGTH)->free(element->maxiumlength); + } + + if (element->vendorpayload) { + capwap_get_message_element(CAPWAP_ELEMENT_VENDORPAYLOAD)->free(element->vendorpayload); + } + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + if (element->binding.ieee80211.wtpradioinformation->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION); + + for (i = 0; i < element->binding.ieee80211.wtpradioinformation->count; i++) { + f->free(*(struct capwap_80211_wtpradioinformation_element**)capwap_array_get_item_pointer(element->binding.ieee80211.wtpradioinformation, i)); + } + } + + capwap_array_free(element->binding.ieee80211.wtpradioinformation); + } + + /* Clean */ + memset(element, 0, sizeof(struct capwap_element_join_response)); +} + +/* */ +void capwap_init_element_configurationstatus_request(struct capwap_element_configurationstatus_request* element, unsigned short binding) { + ASSERT(element != NULL); + + memset(element, 0, sizeof(struct capwap_element_configurationstatus_request)); + + element->radioadmstatus = capwap_array_create(sizeof(struct capwap_radioadmstate_element*), 0); + element->acnamepriority = capwap_array_create(sizeof(struct capwap_acnamepriority_element*), 0); +} + +/* */ +int capwap_parsing_element_configurationstatus_request(struct capwap_element_configurationstatus_request* element, struct capwap_list_item* item) { + ASSERT(element != NULL); + + while (item != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_ACNAME: { + element->acname = (struct capwap_acname_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_RADIOADMSTATE: { + struct capwap_radioadmstate_element** radioadmstate; + + radioadmstate = (struct capwap_radioadmstate_element**)capwap_array_get_item_pointer(element->radioadmstatus, element->radioadmstatus->count); + *radioadmstate = (struct capwap_radioadmstate_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_STATISTICSTIMER: { + element->statisticstimer = (struct capwap_statisticstimer_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPREBOOTSTAT: { + element->wtprebootstat = (struct capwap_wtprebootstat_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_ACNAMEPRIORITY: { + struct capwap_acnamepriority_element** acnamepriority; + + acnamepriority = (struct capwap_acnamepriority_element**)capwap_array_get_item_pointer(element->acnamepriority, element->acnamepriority->count); + *acnamepriority = (struct capwap_acnamepriority_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_TRANSPORT: { + element->trasport = (struct capwap_transport_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPSTATICIPADDRESS: { + element->wtpstaticipaddress = (struct capwap_wtpstaticipaddress_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_VENDORPAYLOAD: { + element->vendorpayload = (struct capwap_vendorpayload_element*)f->parsing(elementitem); + break; + } + } + + /* Next element */ + item = item->next; + } + + return 1; +} + +/* */ +void capwap_free_element_configurationstatus_request(struct capwap_element_configurationstatus_request* element, unsigned short binding) { + unsigned long i; + struct capwap_message_elements_func* f; + + ASSERT(element != NULL); + + if (element->acname) { + capwap_get_message_element(CAPWAP_ELEMENT_ACNAME)->free(element->acname); + } + + if (element->radioadmstatus->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_RADIOADMSTATE); + + for (i = 0; i < element->radioadmstatus->count; i++) { + f->free(*(struct capwap_radioadmstate_element**)capwap_array_get_item_pointer(element->radioadmstatus, i)); + } + } + capwap_array_free(element->radioadmstatus); + + if (element->statisticstimer) { + capwap_get_message_element(CAPWAP_ELEMENT_STATISTICSTIMER)->free(element->statisticstimer); + } + + if (element->wtprebootstat) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPREBOOTSTAT)->free(element->wtprebootstat); + } + + if (element->acnamepriority->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_ACNAMEPRIORITY); + + for (i = 0; i < element->acnamepriority->count; i++) { + f->free(*(struct capwap_acnamepriority_element**)capwap_array_get_item_pointer(element->acnamepriority, i)); + } + } + capwap_array_free(element->acnamepriority); + + if (element->trasport) { + capwap_get_message_element(CAPWAP_ELEMENT_TRANSPORT)->free(element->trasport); + } + + if (element->wtpstaticipaddress) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPSTATICIPADDRESS)->free(element->wtpstaticipaddress); + } + + if (element->vendorpayload) { + capwap_get_message_element(CAPWAP_ELEMENT_VENDORPAYLOAD)->free(element->vendorpayload); + } + + /* Clean */ + memset(element, 0, sizeof(struct capwap_element_configurationstatus_request)); +} + +/* */ +void capwap_init_element_configurationstatus_response(struct capwap_element_configurationstatus_response* element, unsigned short binding) { + ASSERT(element != NULL); + + memset(element, 0, sizeof(struct capwap_element_configurationstatus_response)); + + element->decrypterrorresultperiod = capwap_array_create(sizeof(struct capwap_decrypterrorreport_element*), 0); + element->radiooprstatus = capwap_array_create(sizeof(struct capwap_radiooprstate_element*), 0); +} + +/* */ +int capwap_parsing_element_configurationstatus_response(struct capwap_element_configurationstatus_response* element, struct capwap_list_item* item) { + ASSERT(element != NULL); + + while (item != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_TIMERS: { + element->timers = (struct capwap_timers_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_DECRYPTERRORREPORTPERIOD: { + struct capwap_decrypterrorreport_element** decrypterrorresultperiod; + + decrypterrorresultperiod = (struct capwap_decrypterrorreport_element**)capwap_array_get_item_pointer(element->decrypterrorresultperiod, element->decrypterrorresultperiod->count); + *decrypterrorresultperiod = (struct capwap_decrypterrorreport_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_IDLETIMEOUT: { + element->idletimeout = (struct capwap_idletimeout_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPFALLBACK: { + element->wtpfallback = (struct capwap_wtpfallback_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_ACIPV4LIST: { + element->acipv4list = (capwap_acipv4list_element_array*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_ACIPV6LIST: { + element->acipv6list = (capwap_acipv6list_element_array*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_RADIOOPRSTATE: { + struct capwap_radiooprstate_element** radiooprstatus; + + radiooprstatus = (struct capwap_radiooprstate_element**)capwap_array_get_item_pointer(element->radiooprstatus, element->radiooprstatus->count); + *radiooprstatus = (struct capwap_radiooprstate_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_WTPSTATICIPADDRESS: { + element->wtpstaticipaddress = (struct capwap_wtpstaticipaddress_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_VENDORPAYLOAD: { + element->vendorpayload = (struct capwap_vendorpayload_element*)f->parsing(elementitem); + break; + } + } + + /* Next element */ + item = item->next; + } + + return 1; +} + +/* */ +void capwap_free_element_configurationstatus_response(struct capwap_element_configurationstatus_response* element, unsigned short binding) { + unsigned long i; + struct capwap_message_elements_func* f; + + ASSERT(element != NULL); + + if (element->timers) { + capwap_get_message_element(CAPWAP_ELEMENT_TIMERS)->free(element->timers); + } + + if (element->decrypterrorresultperiod->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_DECRYPTERRORREPORTPERIOD); + + for (i = 0; i < element->decrypterrorresultperiod->count; i++) { + f->free(*(struct capwap_decrypterrorreport_element**)capwap_array_get_item_pointer(element->decrypterrorresultperiod, i)); + } + } + capwap_array_free(element->decrypterrorresultperiod); + + if (element->idletimeout) { + capwap_get_message_element(CAPWAP_ELEMENT_IDLETIMEOUT)->free(element->idletimeout); + } + + if (element->wtpfallback) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPFALLBACK)->free(element->wtpfallback); + } + + if (element->acipv4list) { + capwap_get_message_element(CAPWAP_ELEMENT_ACIPV4LIST)->free(element->acipv4list); + } + + if (element->acipv6list) { + capwap_get_message_element(CAPWAP_ELEMENT_ACIPV6LIST)->free(element->acipv6list); + } + + if (element->radiooprstatus->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_RADIOOPRSTATE); + + for (i = 0; i < element->radiooprstatus->count; i++) { + f->free(*(struct capwap_radiooprstate_element**)capwap_array_get_item_pointer(element->radiooprstatus, i)); + } + } + capwap_array_free(element->radiooprstatus); + + if (element->wtpstaticipaddress) { + capwap_get_message_element(CAPWAP_ELEMENT_WTPSTATICIPADDRESS)->free(element->wtpstaticipaddress); + } + + if (element->vendorpayload) { + capwap_get_message_element(CAPWAP_ELEMENT_VENDORPAYLOAD)->free(element->vendorpayload); + } + + /* Clean */ + memset(element, 0, sizeof(struct capwap_element_configurationstatus_response)); +} + +/* */ +void capwap_init_element_changestateevent_request(struct capwap_element_changestateevent_request* element, unsigned short binding) { + ASSERT(element != NULL); + + memset(element, 0, sizeof(struct capwap_element_changestateevent_request)); + + element->radiooprstatus = capwap_array_create(sizeof(struct capwap_radiooprstate_element*), 0); + element->returnedmessage = capwap_array_create(sizeof(struct capwap_returnedmessage_element*), 0); +} + +/* */ +int capwap_parsing_element_changestateevent_request(struct capwap_element_changestateevent_request* element, struct capwap_list_item* item) { + ASSERT(element != NULL); + + while (item != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_RADIOOPRSTATE: { + struct capwap_radiooprstate_element** radiooprstatus; + + radiooprstatus = (struct capwap_radiooprstate_element**)capwap_array_get_item_pointer(element->radiooprstatus, element->radiooprstatus->count); + *radiooprstatus = (struct capwap_radiooprstate_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_RESULTCODE: { + element->resultcode = (struct capwap_resultcode_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_RETURNEDMESSAGE: { + struct capwap_returnedmessage_element** returnedmessage; + + returnedmessage = (struct capwap_returnedmessage_element**)capwap_array_get_item_pointer(element->returnedmessage, element->returnedmessage->count); + *returnedmessage = (struct capwap_returnedmessage_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_VENDORPAYLOAD: { + element->vendorpayload = (struct capwap_vendorpayload_element*)f->parsing(elementitem); + break; + } + } + + /* Next element */ + item = item->next; + } + + return 1; +} + +/* */ +void capwap_free_element_changestateevent_request(struct capwap_element_changestateevent_request* element, unsigned short binding) { + unsigned long i; + struct capwap_message_elements_func* f; + + ASSERT(element != NULL); + + if (element->radiooprstatus->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_RADIOOPRSTATE); + + for (i = 0; i < element->radiooprstatus->count; i++) { + f->free(*(struct capwap_radiooprstate_element**)capwap_array_get_item_pointer(element->radiooprstatus, i)); + } + } + capwap_array_free(element->radiooprstatus); + + if (element->resultcode) { + capwap_get_message_element(CAPWAP_ELEMENT_RESULTCODE)->free(element->resultcode); + } + + if (element->returnedmessage->count > 0) { + f = capwap_get_message_element(CAPWAP_ELEMENT_RETURNEDMESSAGE); + + for (i = 0; i < element->returnedmessage->count; i++) { + f->free(*(struct capwap_returnedmessage_element**)capwap_array_get_item_pointer(element->returnedmessage, i)); + } + } + capwap_array_free(element->returnedmessage); + + if (element->vendorpayload) { + capwap_get_message_element(CAPWAP_ELEMENT_VENDORPAYLOAD)->free(element->vendorpayload); + } + + /* Clean */ + memset(element, 0, sizeof(struct capwap_element_changestateevent_request)); +} + +/* */ +void capwap_init_element_changestateevent_response(struct capwap_element_changestateevent_response* element, unsigned short binding) { + ASSERT(element != NULL); + + memset(element, 0, sizeof(struct capwap_element_changestateevent_response)); +} + +/* */ +int capwap_parsing_element_changestateevent_response(struct capwap_element_changestateevent_response* element, struct capwap_list_item* item) { + ASSERT(element != NULL); + + while (item != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_VENDORPAYLOAD: { + element->vendorpayload = (struct capwap_vendorpayload_element*)f->parsing(elementitem); + break; + } + } + + /* Next element */ + item = item->next; + } + + return 1; +} + +/* */ +void capwap_free_element_changestateevent_response(struct capwap_element_changestateevent_response* element, unsigned short binding) { + ASSERT(element != NULL); + + if (element->vendorpayload) { + capwap_get_message_element(CAPWAP_ELEMENT_VENDORPAYLOAD)->free(element->vendorpayload); + } + + /* Clean */ + memset(element, 0, sizeof(struct capwap_element_changestateevent_response)); +} + +/* */ +void capwap_init_element_echo_request(struct capwap_element_echo_request* element, unsigned short binding) { + ASSERT(element != NULL); + + memset(element, 0, sizeof(struct capwap_element_echo_request)); +} + +/* */ +int capwap_parsing_element_echo_request(struct capwap_element_echo_request* element, struct capwap_list_item* item) { + ASSERT(element != NULL); + + while (item != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_VENDORPAYLOAD: { + element->vendorpayload = (struct capwap_vendorpayload_element*)f->parsing(elementitem); + break; + } + } + + /* Next element */ + item = item->next; + } + + return 1; +} + +/* */ +void capwap_free_element_echo_request(struct capwap_element_echo_request* element, unsigned short binding) { + ASSERT(element != NULL); + + if (element->vendorpayload) { + capwap_get_message_element(CAPWAP_ELEMENT_VENDORPAYLOAD)->free(element->vendorpayload); + } + + /* Clean */ + memset(element, 0, sizeof(struct capwap_element_echo_request)); +} + +/* */ +void capwap_init_element_echo_response(struct capwap_element_echo_response* element, unsigned short binding) { + ASSERT(element != NULL); + + memset(element, 0, sizeof(struct capwap_element_echo_response)); +} + +/* */ +int capwap_parsing_element_echo_response(struct capwap_element_echo_response* element, struct capwap_list_item* item) { + ASSERT(element != NULL); + + while (item != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_VENDORPAYLOAD: { + element->vendorpayload = (struct capwap_vendorpayload_element*)f->parsing(elementitem); + break; + } + } + + /* Next element */ + item = item->next; + } + + return 1; +} + +/* */ +void capwap_free_element_echo_response(struct capwap_element_echo_response* element, unsigned short binding) { + ASSERT(element != NULL); + + if (element->vendorpayload) { + capwap_get_message_element(CAPWAP_ELEMENT_VENDORPAYLOAD)->free(element->vendorpayload); + } + + /* Clean */ + memset(element, 0, sizeof(struct capwap_element_echo_response)); +} + +/* */ +void capwap_init_element_reset_request(struct capwap_element_reset_request* element, unsigned short binding) { + ASSERT(element != NULL); + + memset(element, 0, sizeof(struct capwap_element_reset_request)); +} + +/* */ +int capwap_parsing_element_reset_request(struct capwap_element_reset_request* element, struct capwap_list_item* item) { + ASSERT(element != NULL); + + while (item != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_IMAGEIDENTIFIER: { + element->imageidentifier = (struct capwap_imageidentifier_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_VENDORPAYLOAD: { + element->vendorpayload = (struct capwap_vendorpayload_element*)f->parsing(elementitem); + break; + } + } + + /* Next element */ + item = item->next; + } + + return 1; +} + +/* */ +void capwap_free_element_reset_request(struct capwap_element_reset_request* element, unsigned short binding) { + ASSERT(element != NULL); + + if (element->imageidentifier) { + capwap_get_message_element(CAPWAP_ELEMENT_IMAGEIDENTIFIER)->free(element->imageidentifier); + } + + if (element->vendorpayload) { + capwap_get_message_element(CAPWAP_ELEMENT_VENDORPAYLOAD)->free(element->vendorpayload); + } + + /* Clean */ + memset(element, 0, sizeof(struct capwap_element_reset_request)); +} + +/* */ +void capwap_init_element_reset_response(struct capwap_element_reset_response* element, unsigned short binding) { + ASSERT(element != NULL); + + memset(element, 0, sizeof(struct capwap_element_reset_response)); +} + +/* */ +int capwap_parsing_element_reset_response(struct capwap_element_reset_response* element, struct capwap_list_item* item) { + ASSERT(element != NULL); + + while (item != NULL) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_RESULTCODE: { + element->resultcode = (struct capwap_resultcode_element*)f->parsing(elementitem); + break; + } + + case CAPWAP_ELEMENT_VENDORPAYLOAD: { + element->vendorpayload = (struct capwap_vendorpayload_element*)f->parsing(elementitem); + break; + } + } + + /* Next element */ + item = item->next; + } + + return 1; +} + +/* */ +void capwap_free_element_reset_response(struct capwap_element_reset_response* element, unsigned short binding) { + ASSERT(element != NULL); + + if (element->resultcode) { + capwap_get_message_element(CAPWAP_ELEMENT_RESULTCODE)->free(element->resultcode); + } + + if (element->vendorpayload) { + capwap_get_message_element(CAPWAP_ELEMENT_VENDORPAYLOAD)->free(element->vendorpayload); + } + + /* Clean */ + memset(element, 0, sizeof(struct capwap_element_reset_response)); +} diff --git a/src/common/capwap_element.h b/src/common/capwap_element.h new file mode 100644 index 0000000..aebd2d8 --- /dev/null +++ b/src/common/capwap_element.h @@ -0,0 +1,288 @@ +#ifndef __CAPWAP_ELEMENT_HEADER__ +#define __CAPWAP_ELEMENT_HEADER__ + +#include "capwap_array.h" +#include "capwap_list.h" + +/* Standard message elements 1 -> 52 (1 - 1023) */ +#define CAPWAP_MESSAGE_ELEMENTS_START 1 +#define CAPWAP_MESSAGE_ELEMENTS_STOP 53 +#define CAPWAP_MESSAGE_ELEMENTS_COUNT ((CAPWAP_MESSAGE_ELEMENTS_STOP - CAPWAP_MESSAGE_ELEMENTS_START) + 1) +#define IS_MESSAGE_ELEMENTS(x) (((x >= CAPWAP_MESSAGE_ELEMENTS_START) && (x <= CAPWAP_MESSAGE_ELEMENTS_STOP)) ? 1 : 0) + +/* 802.11 message elements 1024 -> 1024 (1024 - 2047) */ +#define CAPWAP_80211_MESSAGE_ELEMENTS_START 1024 +#define CAPWAP_80211_MESSAGE_ELEMENTS_STOP 1048 +#define CAPWAP_80211_MESSAGE_ELEMENTS_COUNT ((CAPWAP_80211_MESSAGE_ELEMENTS_STOP - CAPWAP_80211_MESSAGE_ELEMENTS_START) + 1) +#define IS_80211_MESSAGE_ELEMENTS(x) (((x >= CAPWAP_80211_MESSAGE_ELEMENTS_START) && (x <= CAPWAP_80211_MESSAGE_ELEMENTS_STOP)) ? 1 : 0) + +/* Message element */ +struct capwap_message_element { + unsigned short type; + unsigned short length; + char data[0]; +} __attribute__((__packed__)); + +typedef struct capwap_message_element*(*capwap_create_message_element)(void* data, unsigned long length); +typedef int(*capwap_validate_message_element)(struct capwap_message_element* element); +typedef void*(*capwap_parsing_message_element)(struct capwap_message_element* element); +typedef void(*capwap_free_message_element)(void*); + +struct capwap_message_elements_func { + capwap_create_message_element create; + capwap_validate_message_element check; + capwap_parsing_message_element parsing; + capwap_free_message_element free; +}; + +struct capwap_message_elements_func* capwap_get_message_element(unsigned long code); + +/*********************************************************************************************************************/ +/* Standard message elements */ +#include "capwap_element_acdescriptor.h" /* 00001 */ +#include "capwap_element_acipv4list.h" /* 00002 */ +#include "capwap_element_acipv6list.h" /* 00003 */ +#include "capwap_element_acname.h" /* 00004 */ +#include "capwap_element_acnamepriority.h" /* 00005 */ +/* 00006 */ +/* 00007 */ +/* 00008 */ +/* Reserved */ /* 00009 */ +#include "capwap_element_controlipv4.h" /* 00010 */ +#include "capwap_element_controlipv6.h" /* 00011 */ +#include "capwap_element_timers.h" /* 00012 */ +/* 00013 */ +/* 00014 */ +/* 00015 */ +#include "capwap_element_decrypterrorreportperiod.h" /* 00016 */ +/* 00017 */ +/* 00018 */ +/* Reserved */ /* 00019 */ +#include "capwap_element_discoverytype.h" /* 00020 */ +/* 00021 */ +/* 00022 */ +#include "capwap_element_idletimeout.h" /* 00023 */ +/* 00024 */ +#include "capwap_element_imageidentifier.h" /* 00025 */ +/* 00026 */ +/* 00027 */ +#include "capwap_element_location.h" /* 00028 */ +#include "capwap_element_maximumlength.h" /* 00029 */ +#include "capwap_element_localipv4.h" /* 00030 */ +#include "capwap_element_radioadmstate.h" /* 00031 */ +#include "capwap_element_radiooprstate.h" /* 00032 */ +#include "capwap_element_resultcode.h" /* 00033 */ +#include "capwap_element_returnedmessage.h" /* 00034 */ +#include "capwap_element_sessionid.h" /* 00035 */ +#include "capwap_element_statisticstimer.h" /* 00036 */ +#include "capwap_element_vendorpayload.h" /* 00037 */ +#include "capwap_element_wtpboarddata.h" /* 00038 */ +#include "capwap_element_wtpdescriptor.h" /* 00039 */ +#include "capwap_element_wtpfallback.h" /* 00040 */ +#include "capwap_element_wtpframetunnelmode.h" /* 00041 */ +/* Reserved */ /* 00042 */ +/* Reserved */ /* 00043 */ +#include "capwap_element_wtpmactype.h" /* 00044 */ +#include "capwap_element_wtpname.h" /* 00045 */ +/* Reserved */ /* 00046 */ +/* 00047 */ +#include "capwap_element_wtprebootstat.h" /* 00048 */ +#include "capwap_element_wtpstaticipaddress.h" /* 00049 */ +#include "capwap_element_localipv6.h" /* 00050 */ +#include "capwap_element_transport.h" /* 00051 */ +#include "capwap_element_mtudiscovery.h" /* 00052 */ +#include "capwap_element_ecnsupport.h" /* 00053 */ + +/* IEEE 802.11 message elements */ +#include "capwap_element_80211_wtpradioinformation.h" /* 01048 */ + +/*********************************************************************************************************************/ +struct capwap_element_discovery_request { + struct capwap_discoverytype_element* discoverytype; + struct capwap_wtpboarddata_element* wtpboarddata; + struct capwap_wtpdescriptor_element* wtpdescriptor; + struct capwap_wtpframetunnelmode_element* wtpframetunnel; + struct capwap_wtpmactype_element* wtpmactype; + struct capwap_mtudiscovery_element* mtudiscovery; + struct capwap_vendorpayload_element* vendorpayload; + + union { + struct { + struct capwap_array* wtpradioinformation; + } ieee80211; + } binding; +}; + +void capwap_init_element_discovery_request(struct capwap_element_discovery_request* element, unsigned short binding); +int capwap_parsing_element_discovery_request(struct capwap_element_discovery_request* element, struct capwap_list_item* item); +void capwap_free_element_discovery_request(struct capwap_element_discovery_request* element, unsigned short binding); + +/* */ +struct capwap_element_discovery_response { + struct capwap_acdescriptor_element* acdescriptor; + struct capwap_acname_element* acname; + struct capwap_array* controlipv4; + struct capwap_array* controlipv6; + struct capwap_vendorpayload_element* vendorpayload; + + union { + struct { + struct capwap_array* wtpradioinformation; + } ieee80211; + } binding; +}; + +void capwap_init_element_discovery_response(struct capwap_element_discovery_response* element, unsigned short binding); +int capwap_parsing_element_discovery_response(struct capwap_element_discovery_response* element, struct capwap_list_item* item); +void capwap_free_element_discovery_response(struct capwap_element_discovery_response* element, unsigned short binding); + +/* */ +struct capwap_element_join_request { + struct capwap_location_element* locationdata; + struct capwap_wtpboarddata_element* wtpboarddata; + struct capwap_wtpdescriptor_element* wtpdescriptor; + struct capwap_wtpname_element* wtpname; + struct capwap_sessionid_element* sessionid; + struct capwap_wtpframetunnelmode_element* wtpframetunnel; + struct capwap_wtpmactype_element* wtpmactype; + struct capwap_ecnsupport_element* ecnsupport; + struct capwap_localipv4_element* localipv4; + struct capwap_localipv6_element* localipv6; + struct capwap_transport_element* trasport; + struct capwap_maximumlength_element* maxiumlength; + struct capwap_wtprebootstat_element* wtprebootstat; + struct capwap_vendorpayload_element* vendorpayload; + + union { + struct { + struct capwap_array* wtpradioinformation; + } ieee80211; + } binding; +}; + +void capwap_init_element_join_request(struct capwap_element_join_request* element, unsigned short binding); +int capwap_parsing_element_join_request(struct capwap_element_join_request* element, struct capwap_list_item* item); +void capwap_free_element_join_request(struct capwap_element_join_request* element, unsigned short binding); + +/* */ +struct capwap_element_join_response { + struct capwap_resultcode_element* resultcode; + struct capwap_array* returnedmessage; + struct capwap_acdescriptor_element* acdescriptor; + struct capwap_acname_element* acname; + struct capwap_ecnsupport_element* ecnsupport; + struct capwap_array* controlipv4; + struct capwap_array* controlipv6; + struct capwap_localipv4_element* localipv4; + struct capwap_localipv6_element* localipv6; + capwap_acipv4list_element_array* acipv4list; + capwap_acipv6list_element_array* acipv6list; + struct capwap_transport_element* trasport; + struct capwap_imageidentifier_element* imageidentifier; + struct capwap_maximumlength_element* maxiumlength; + struct capwap_vendorpayload_element* vendorpayload; + + union { + struct { + struct capwap_array* wtpradioinformation; + } ieee80211; + } binding; +}; + +void capwap_init_element_join_response(struct capwap_element_join_response* element, unsigned short binding); +int capwap_parsing_element_join_response(struct capwap_element_join_response* element, struct capwap_list_item* item); +void capwap_free_element_join_response(struct capwap_element_join_response* element, unsigned short binding); + +/* */ +struct capwap_element_configurationstatus_request { + struct capwap_acname_element* acname; + struct capwap_array* radioadmstatus; + struct capwap_statisticstimer_element* statisticstimer; + struct capwap_wtprebootstat_element* wtprebootstat; + struct capwap_array* acnamepriority; + struct capwap_transport_element* trasport; + struct capwap_wtpstaticipaddress_element* wtpstaticipaddress; + struct capwap_vendorpayload_element* vendorpayload; +}; + +void capwap_init_element_configurationstatus_request(struct capwap_element_configurationstatus_request* element, unsigned short binding); +int capwap_parsing_element_configurationstatus_request(struct capwap_element_configurationstatus_request* element, struct capwap_list_item* item); +void capwap_free_element_configurationstatus_request(struct capwap_element_configurationstatus_request* element, unsigned short binding); + +/* */ +struct capwap_element_configurationstatus_response { + struct capwap_timers_element* timers; + struct capwap_array* decrypterrorresultperiod; + struct capwap_idletimeout_element* idletimeout; + struct capwap_wtpfallback_element* wtpfallback; + capwap_acipv4list_element_array* acipv4list; + capwap_acipv6list_element_array* acipv6list; + struct capwap_array* radiooprstatus; + struct capwap_wtpstaticipaddress_element* wtpstaticipaddress; + struct capwap_vendorpayload_element* vendorpayload; +}; + +void capwap_init_element_configurationstatus_response(struct capwap_element_configurationstatus_response* element, unsigned short binding); +int capwap_parsing_element_configurationstatus_response(struct capwap_element_configurationstatus_response* element, struct capwap_list_item* item); +void capwap_free_element_configurationstatus_response(struct capwap_element_configurationstatus_response* element, unsigned short binding); + +/* */ +struct capwap_element_changestateevent_request { + struct capwap_array* radiooprstatus; + struct capwap_resultcode_element* resultcode; + struct capwap_array* returnedmessage; + struct capwap_vendorpayload_element* vendorpayload; +}; + +void capwap_init_element_changestateevent_request(struct capwap_element_changestateevent_request* element, unsigned short binding); +int capwap_parsing_element_changestateevent_request(struct capwap_element_changestateevent_request* element, struct capwap_list_item* item); +void capwap_free_element_changestateevent_request(struct capwap_element_changestateevent_request* element, unsigned short binding); + +/* */ +struct capwap_element_changestateevent_response { + struct capwap_vendorpayload_element* vendorpayload; +}; + +void capwap_init_element_changestateevent_response(struct capwap_element_changestateevent_response* element, unsigned short binding); +int capwap_parsing_element_changestateevent_response(struct capwap_element_changestateevent_response* element, struct capwap_list_item* item); +void capwap_free_element_changestateevent_response(struct capwap_element_changestateevent_response* element, unsigned short binding); + +/* */ +struct capwap_element_echo_request { + struct capwap_vendorpayload_element* vendorpayload; +}; + +void capwap_init_element_echo_request(struct capwap_element_echo_request* element, unsigned short binding); +int capwap_parsing_element_echo_request(struct capwap_element_echo_request* element, struct capwap_list_item* item); +void capwap_free_element_echo_request(struct capwap_element_echo_request* element, unsigned short binding); + +/* */ +struct capwap_element_echo_response { + struct capwap_vendorpayload_element* vendorpayload; +}; + +void capwap_init_element_echo_response(struct capwap_element_echo_response* element, unsigned short binding); +int capwap_parsing_element_echo_response(struct capwap_element_echo_response* element, struct capwap_list_item* item); +void capwap_free_element_echo_response(struct capwap_element_echo_response* element, unsigned short binding); + +/* */ +struct capwap_element_reset_request { + struct capwap_imageidentifier_element* imageidentifier; + struct capwap_vendorpayload_element* vendorpayload; +}; + +void capwap_init_element_reset_request(struct capwap_element_reset_request* element, unsigned short binding); +int capwap_parsing_element_reset_request(struct capwap_element_reset_request* element, struct capwap_list_item* item); +void capwap_free_element_reset_request(struct capwap_element_reset_request* element, unsigned short binding); + +/* */ +struct capwap_element_reset_response { + struct capwap_resultcode_element* resultcode; + struct capwap_vendorpayload_element* vendorpayload; +}; + +void capwap_init_element_reset_response(struct capwap_element_reset_response* element, unsigned short binding); +int capwap_parsing_element_reset_response(struct capwap_element_reset_response* element, struct capwap_list_item* item); +void capwap_free_element_reset_response(struct capwap_element_reset_response* element, unsigned short binding); + +#endif /* __CAPWAP_ELEMENT_HEADER__ */ diff --git a/src/common/capwap_element_80211_wtpradioinformation.c b/src/common/capwap_element_80211_wtpradioinformation.c new file mode 100644 index 0000000..4ad3e72 --- /dev/null +++ b/src/common/capwap_element_80211_wtpradioinformation.c @@ -0,0 +1,88 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Radio ID | Radio Type | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Radio Type | ++-+-+-+-+-+-+-+-+ + +Type: 1048 for IEEE 802.11 WTP Radio Information + +Length: 5 + +********************************************************************/ + +struct capwap_80211_wtpradioinformation_raw_element { + unsigned char radioid; + unsigned long radiotype; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_80211_wtpradioinformation_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_80211_wtpradioinformation_raw_element* dataraw; + struct capwap_80211_wtpradioinformation_element* dataelement = (struct capwap_80211_wtpradioinformation_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength >= sizeof(struct capwap_80211_wtpradioinformation_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_80211_wtpradioinformation_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_80211_wtpradioinformation_raw_element)); + element->type = htons(CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION); + element->length = htons(sizeof(struct capwap_80211_wtpradioinformation_raw_element)); + dataraw = (struct capwap_80211_wtpradioinformation_raw_element*)element->data; + + dataraw->radioid = dataelement->radioid; + dataraw->radiotype = htonl(dataelement->radiotype); + return element; +} + +/* */ +int capwap_80211_wtpradioinformation_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_80211_wtpradioinformation_element_parsing(struct capwap_message_element* element) { + struct capwap_80211_wtpradioinformation_element* data; + struct capwap_80211_wtpradioinformation_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION); + + if (ntohs(element->length) != 5) { + return NULL; + } + + dataraw = (struct capwap_80211_wtpradioinformation_raw_element*)element->data; + + /* */ + data = (struct capwap_80211_wtpradioinformation_element*)capwap_alloc(sizeof(struct capwap_80211_wtpradioinformation_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->radioid = dataraw->radioid; + data->radiotype = ntohl(dataraw->radiotype); + return data; +} + +/* */ +void capwap_80211_wtpradioinformation_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_80211_wtpradioinformation.h b/src/common/capwap_element_80211_wtpradioinformation.h new file mode 100644 index 0000000..3e07023 --- /dev/null +++ b/src/common/capwap_element_80211_wtpradioinformation.h @@ -0,0 +1,27 @@ +#ifndef __CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION_HEADER__ +#define __CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION_HEADER__ + +#define CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION 1048 + +struct capwap_80211_wtpradioinformation_element { + unsigned char radioid; + unsigned long radiotype; +}; + +#define CAPWAP_RADIO_TYPE_80211N 0x08 +#define CAPWAP_RADIO_TYPE_80211G 0x04 +#define CAPWAP_RADIO_TYPE_80211A 0x02 +#define CAPWAP_RADIO_TYPE_80211B 0x01 + +struct capwap_message_element* capwap_80211_wtpradioinformation_element_create(void* data, unsigned long length); +int capwap_80211_wtpradioinformation_element_validate(struct capwap_message_element* element); +void* capwap_80211_wtpradioinformation_element_parsing(struct capwap_message_element* element); +void capwap_80211_wtpradioinformation_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_80211_WTPRADIOINFORMATION_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION); \ + f->create(x, sizeof(struct capwap_80211_wtpradioinformation_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION_HEADER__ */ diff --git a/src/common/capwap_element_acdescriptor.c b/src/common/capwap_element_acdescriptor.c new file mode 100644 index 0000000..649cd74 --- /dev/null +++ b/src/common/capwap_element_acdescriptor.c @@ -0,0 +1,199 @@ +#include "capwap.h" +#include "capwap_array.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Stations | Limit | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Active WTPs | Max WTPs | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Security | R-MAC Field | Reserved1 | DTLS Policy | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| AC Information Sub-Element... ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| AC Information Vendor Identifier | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| AC Information Type | AC Information Length | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| AC Information Data... ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 1 for AC Descriptor +Length: >= 12 + +********************************************************************/ + +struct capwap_acdescriptor_raw_element { + unsigned short stations; + unsigned short limit; + unsigned short activewtp; + unsigned short maxwtp; + unsigned char security; + unsigned char rmacfield; + unsigned char reserved; + unsigned char dtlspolicy; + char data[0]; +} __attribute__((__packed__)); + +struct capwap_acdescriptor_raw_desc_subelement { + unsigned long vendor; + unsigned short type; + unsigned short length; + char data[0]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_acdescriptor_element_create(void* data, unsigned long datalength) { + char* pos; + unsigned long i; + unsigned short length; + struct capwap_message_element* element; + struct capwap_acdescriptor_raw_element* dataraw; + struct capwap_acdescriptor_element* dataelement = (struct capwap_acdescriptor_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength >= sizeof(struct capwap_acdescriptor_element)); + ASSERT(dataelement->descsubelement != NULL); + + /* Calc length packet */ + length = sizeof(struct capwap_acdescriptor_raw_element); + for (i = 0; i < dataelement->descsubelement->count; i++) { + struct capwap_acdescriptor_desc_subelement* desc = (struct capwap_acdescriptor_desc_subelement*)capwap_array_get_item_pointer(dataelement->descsubelement, i); + length += sizeof(struct capwap_acdescriptor_raw_desc_subelement) + desc->length; + } + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + length); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + length); + element->type = htons(CAPWAP_ELEMENT_ACDESCRIPTION); + element->length = htons(length); + + /* Descriptor */ + dataraw = (struct capwap_acdescriptor_raw_element*)element->data; + dataraw->stations = htons(dataelement->station); + dataraw->limit = htons(dataelement->stationlimit); + dataraw->activewtp = htons(dataelement->wtp); + dataraw->maxwtp = htons(dataelement->wtplimit); + dataraw->security = dataelement->security; + dataraw->rmacfield = dataelement->rmacfield; + dataraw->dtlspolicy = dataelement->dtlspolicy; + + /* Descriptor Sub-Element */ + pos = dataraw->data; + for (i = 0; i < dataelement->descsubelement->count; i++) { + struct capwap_acdescriptor_raw_desc_subelement* descraw = (struct capwap_acdescriptor_raw_desc_subelement*)pos; + struct capwap_acdescriptor_desc_subelement* desc = (struct capwap_acdescriptor_desc_subelement*)capwap_array_get_item_pointer(dataelement->descsubelement, i); + + descraw->vendor = htonl(desc->vendor); + descraw->type = htons(desc->type); + descraw->length = htons(desc->length); + memcpy(descraw->data, desc->data, desc->length); + + pos += sizeof(struct capwap_acdescriptor_raw_desc_subelement) + desc->length; + } + + return element; +} + +/* */ +int capwap_acdescriptor_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_acdescriptor_element_parsing(struct capwap_message_element* element) { + unsigned char i; + long length; + char* pos; + struct capwap_acdescriptor_element* data; + struct capwap_acdescriptor_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_ACDESCRIPTION); + + length = (long)ntohs(element->length); + if (length < 12) { + capwap_logging_debug("Invalid AC Descriptor element"); + return NULL; + } + + /* */ + dataraw = (struct capwap_acdescriptor_raw_element*)element->data; + if ((dataraw->stations > dataraw->limit) || (dataraw->activewtp > dataraw->maxwtp)) { + capwap_logging_debug("Invalid AC Descriptor element"); + return NULL; + } + + /* */ + data = (struct capwap_acdescriptor_element*)capwap_alloc(sizeof(struct capwap_acdescriptor_element)); + if (!data) { + capwap_outofmemory(); + } + + data->descsubelement = capwap_array_create(sizeof(struct capwap_acdescriptor_desc_subelement), 0); + + /* */ + data->station = htons(dataraw->stations); + data->stationlimit = htons(dataraw->limit); + data->wtp = htons(dataraw->activewtp); + data->wtplimit = htons(dataraw->maxwtp); + data->security = dataraw->security; + data->rmacfield = dataraw->rmacfield; + data->dtlspolicy = dataraw->dtlspolicy; + + pos = dataraw->data; + length -= sizeof(struct capwap_acdescriptor_raw_element); + + /* Description Subelement */ + i = 0; + while (length > 0) { + struct capwap_acdescriptor_desc_subelement* desc = (struct capwap_acdescriptor_desc_subelement*)capwap_array_get_item_pointer(data->descsubelement, i); + struct capwap_acdescriptor_raw_desc_subelement* descraw = (struct capwap_acdescriptor_raw_desc_subelement*)pos; + unsigned short desclength = ntohs(descraw->length); + unsigned short descrawlength = sizeof(struct capwap_acdescriptor_raw_desc_subelement) + desclength; + + if ((desclength > CAPWAP_ACDESC_SUBELEMENT_MAXDATA) || (length < descrawlength)) { + capwap_logging_debug("Invalid AC Descriptor element"); + capwap_acdescriptor_element_free(data); + return NULL; + } + + /* */ + desc->vendor = ntohl(descraw->vendor); + desc->type = ntohs(descraw->type); + desc->length = desclength; + memcpy(desc->data, descraw->data, desclength); + + /* */ + i++; + pos += descrawlength; + length -= descrawlength; + } + + return data; +} + +/* */ +void capwap_acdescriptor_element_free(void* data) { + struct capwap_acdescriptor_element* dataelement = (struct capwap_acdescriptor_element*)data; + + ASSERT(dataelement != NULL); + ASSERT(dataelement->descsubelement != NULL); + + capwap_array_free(dataelement->descsubelement); + capwap_free(dataelement); +} diff --git a/src/common/capwap_element_acdescriptor.h b/src/common/capwap_element_acdescriptor.h new file mode 100644 index 0000000..cc7fb55 --- /dev/null +++ b/src/common/capwap_element_acdescriptor.h @@ -0,0 +1,48 @@ +#ifndef __CAPWAP_ELEMENT_ACDESCRIPTOR_HEADER__ +#define __CAPWAP_ELEMENT_ACDESCRIPTOR_HEADER__ + +#define CAPWAP_ELEMENT_ACDESCRIPTION 1 + +#define CAPWAP_ACDESC_SECURITY_PRESHARED_KEY 0x04 +#define CAPWAP_ACDESC_SECURITY_X509_CERT 0x02 + +#define CAPWAP_ACDESC_RMACFIELD_SUPPORTED 1 +#define CAPWAP_ACDESC_RMACFIELD_NOTSUPPORTED 2 + +#define CAPWAP_ACDESC_DTLS_DATA_CHANNEL_ENABLED 0x04 +#define CAPWAP_ACDESC_CLEAR_DATA_CHANNEL_ENABLED 0x02 + +struct capwap_acdescriptor_element { + unsigned short station; + unsigned short stationlimit; + unsigned short wtp; + unsigned short wtplimit; + unsigned char security; + unsigned char rmacfield; + unsigned char dtlspolicy; + struct capwap_array* descsubelement; +}; + +#define CAPWAP_ACDESC_SUBELEMENT_HARDWAREVERSION 4 +#define CAPWAP_ACDESC_SUBELEMENT_SOFTWAREVERSION 5 +#define CAPWAP_ACDESC_SUBELEMENT_MAXDATA 1024 + +struct capwap_acdescriptor_desc_subelement { + unsigned long vendor; + unsigned short type; + unsigned short length; + char data[CAPWAP_ACDESC_SUBELEMENT_MAXDATA]; +}; + +struct capwap_message_element* capwap_acdescriptor_element_create(void* data, unsigned long length); +int capwap_acdescriptor_element_validate(struct capwap_message_element* element); +void* capwap_acdescriptor_element_parsing(struct capwap_message_element* element); +void capwap_acdescriptor_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_ACDESCRIPTOR_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_ACDESCRIPTION); \ + f->create(x, sizeof(struct capwap_acdescriptor_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_ACDESCRIPTOR_HEADER__ */ diff --git a/src/common/capwap_element_acipv4list.c b/src/common/capwap_element_acipv4list.c new file mode 100644 index 0000000..f4ae181 --- /dev/null +++ b/src/common/capwap_element_acipv4list.c @@ -0,0 +1,103 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address[] | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 2 for AC IPv4 List +Length: >= 4 + +********************************************************************/ + +struct capwap_acipv4list_raw_element { + unsigned long address; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_acipv4list_element_create(void* data, unsigned long datalength) { + int i; + int items; + unsigned short sizeitems; + struct capwap_message_element* element; + capwap_acipv4list_element_array* dataarray = (capwap_acipv4list_element_array*)data; + struct capwap_acipv4list_raw_element* dataraw; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(capwap_acipv4list_element_array)); + + items = min(dataarray->count, CAPWAP_ACIPV4LIST_MAX_ELEMENTS); + + /* Alloc block of memory */ + sizeitems = sizeof(struct capwap_acipv4list_raw_element) * items; + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeitems); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeitems); + element->type = htons(CAPWAP_ELEMENT_ACIPV4LIST); + element->length = htons(sizeitems); + + dataraw = (struct capwap_acipv4list_raw_element*)element->data; + for (i = 0; i < items; i++) { + struct capwap_acipv4list_element* dataelement = (struct capwap_acipv4list_element*)capwap_array_get_item_pointer(dataarray, i); + dataraw->address = dataelement->address.s_addr; + + /* Next raw item */ + dataraw++; + } + + return element; +} + +/* */ +int capwap_acipv4list_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_acipv4list_element_parsing(struct capwap_message_element* element) { + int i; + int items; + unsigned short length; + capwap_acipv4list_element_array* data; + struct capwap_acipv4list_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_ACIPV4LIST); + + length = ntohs(element->length); + if ((length > 0) && ((length % sizeof(struct capwap_acipv4list_raw_element)) != 0)) { + return NULL; + } + + /* */ + items = length / sizeof(struct capwap_acipv4list_raw_element); + data = (capwap_acipv4list_element_array*)capwap_array_create(sizeof(struct capwap_acipv4list_element), items); + + /* */ + dataraw = (struct capwap_acipv4list_raw_element*)element->data; + for (i = 0; i < items; i++) { + struct capwap_acipv4list_element* dataelement = (struct capwap_acipv4list_element*)capwap_array_get_item_pointer(data, i); + dataelement->address.s_addr = dataraw->address; + + /* Next raw item */ + dataraw++; + } + + return data; +} + +/* */ +void capwap_acipv4list_element_free(void* data) { + ASSERT(data != NULL); + + capwap_array_free((capwap_acipv4list_element_array*)data); +} diff --git a/src/common/capwap_element_acipv4list.h b/src/common/capwap_element_acipv4list.h new file mode 100644 index 0000000..911f659 --- /dev/null +++ b/src/common/capwap_element_acipv4list.h @@ -0,0 +1,25 @@ +#ifndef __CAPWAP_ELEMENT_ACIPV4LIST_HEADER__ +#define __CAPWAP_ELEMENT_ACIPV4LIST_HEADER__ + +#define CAPWAP_ELEMENT_ACIPV4LIST 2 + +#define CAPWAP_ACIPV4LIST_MAX_ELEMENTS 1024 + +typedef struct capwap_array capwap_acipv4list_element_array; +struct capwap_acipv4list_element { + struct in_addr address; +}; + +struct capwap_message_element* capwap_acipv4list_element_create(void* data, unsigned long datalength); +int capwap_acipv4list_element_validate(struct capwap_message_element* element); +void* capwap_acipv4list_element_parsing(struct capwap_message_element* element); +void capwap_acipv4list_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_ACIPV4LIST_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_ACIPV4LIST); \ + f->create(x, sizeof(capwap_acipv4list_element_array)); \ + }) + +#endif /* __CAPWAP_ELEMENT_ACIPV4LIST_HEADER__ */ diff --git a/src/common/capwap_element_acipv6list.c b/src/common/capwap_element_acipv6list.c new file mode 100644 index 0000000..f4ebed2 --- /dev/null +++ b/src/common/capwap_element_acipv6list.c @@ -0,0 +1,109 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 3 for AC IPV6 List +Length: >= 16 + +********************************************************************/ + +struct capwap_acipv6list_raw_element { + unsigned long address[4]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_acipv6list_element_create(void* data, unsigned long datalength) { + int i; + int items; + unsigned short sizeitems; + struct capwap_message_element* element; + capwap_acipv6list_element_array* dataarray = (capwap_acipv6list_element_array*)data; + struct capwap_acipv6list_raw_element* dataraw; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(capwap_acipv6list_element_array)); + + items = min(dataarray->count, CAPWAP_ACIPV6LIST_MAX_ELEMENTS); + + /* Alloc block of memory */ + sizeitems = sizeof(struct capwap_acipv6list_raw_element) * items; + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeitems); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeitems); + element->type = htons(CAPWAP_ELEMENT_ACIPV6LIST); + element->length = htons(sizeitems); + + dataraw = (struct capwap_acipv6list_raw_element*)element->data; + for (i = 0; i < items; i++) { + struct capwap_acipv6list_element* dataelement = (struct capwap_acipv6list_element*)capwap_array_get_item_pointer(dataarray, i); + memcpy(dataraw->address, dataelement->address.s6_addr32, sizeof(unsigned long) * 4); + + /* Next raw item */ + dataraw++; + } + + return element; +} + +/* */ +int capwap_acipv6list_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_acipv6list_element_parsing(struct capwap_message_element* element) { + int i; + int items; + unsigned short length; + capwap_acipv6list_element_array* data; + struct capwap_acipv6list_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_ACIPV6LIST); + + length = ntohs(element->length); + if ((length > 0) && ((length % sizeof(struct capwap_acipv6list_raw_element)) != 0)) { + return NULL; + } + + /* */ + items = length / sizeof(struct capwap_acipv6list_raw_element); + data = (capwap_acipv6list_element_array*)capwap_array_create(sizeof(struct capwap_acipv6list_element), items); + + /* */ + dataraw = (struct capwap_acipv6list_raw_element*)element->data; + for (i = 0; i < items; i++) { + struct capwap_acipv6list_element* dataelement = (struct capwap_acipv6list_element*)capwap_array_get_item_pointer(data, i); + memcpy(dataelement->address.s6_addr32, dataraw->address, sizeof(unsigned long) * 4); + + /* Next raw item */ + dataraw++; + } + + return data; +} + +/* */ +void capwap_acipv6list_element_free(void* data) { + ASSERT(data != NULL); + + capwap_array_free((capwap_acipv6list_element_array*)data); +} diff --git a/src/common/capwap_element_acipv6list.h b/src/common/capwap_element_acipv6list.h new file mode 100644 index 0000000..55f8aa6 --- /dev/null +++ b/src/common/capwap_element_acipv6list.h @@ -0,0 +1,25 @@ +#ifndef __CAPWAP_ELEMENT_ACIPV6LIST_HEADER__ +#define __CAPWAP_ELEMENT_ACIPV6LIST_HEADER__ + +#define CAPWAP_ELEMENT_ACIPV6LIST 3 + +#define CAPWAP_ACIPV6LIST_MAX_ELEMENTS 1024 + +typedef struct capwap_array capwap_acipv6list_element_array; +struct capwap_acipv6list_element { + struct in6_addr address; +}; + +struct capwap_message_element* capwap_acipv6list_element_create(void* data, unsigned long datalength); +int capwap_acipv6list_element_validate(struct capwap_message_element* element); +void* capwap_acipv6list_element_parsing(struct capwap_message_element* element); +void capwap_acipv6list_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_ACIPV6LIST_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_ACIPV6LIST); \ + f->create(x, sizeof(capwap_acipv6list_element_array)); \ + }) + +#endif /* __CAPWAP_ELEMENT_ACIPV6LIST_HEADER__ */ diff --git a/src/common/capwap_element_acname.c b/src/common/capwap_element_acname.c new file mode 100644 index 0000000..be21527 --- /dev/null +++ b/src/common/capwap_element_acname.c @@ -0,0 +1,86 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 + 0 1 2 3 4 5 6 7 ++-+-+-+-+-+-+-+-+ +| Name ... ++-+-+-+-+-+-+-+-+ + +Type: 4 for AC Name +Length: >= 1 + +********************************************************************/ + +struct capwap_acname_raw_element { + char name[0]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_acname_element_create(void* data, unsigned long datalength) { + unsigned short namelength; + struct capwap_message_element* element; + struct capwap_acname_raw_element* dataraw; + struct capwap_acname_element* dataelement = (struct capwap_acname_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_acname_element)); + + /* Alloc block of memory */ + namelength = strlen(dataelement->name); + element = capwap_alloc(sizeof(struct capwap_message_element) + namelength); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + namelength); + element->type = htons(CAPWAP_ELEMENT_ACNAME); + element->length = htons(namelength); + + dataraw = (struct capwap_acname_raw_element*)element->data; + memcpy(&dataraw->name[0], &dataelement->name[0], namelength); + return element; +} + +/* */ +int capwap_acname_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_acname_element_parsing(struct capwap_message_element* element) { + unsigned short namelength; + struct capwap_acname_element* data; + struct capwap_acname_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_ACNAME); + + namelength = ntohs(element->length); + if (!namelength || (namelength > CAPWAP_ACNAME_MAXLENGTH)) { + return NULL; + } + + /* */ + dataraw = (struct capwap_acname_raw_element*)element->data; + data = (struct capwap_acname_element*)capwap_alloc(sizeof(struct capwap_acname_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + memcpy(&data->name[0], &dataraw->name[0], namelength); + data->name[namelength] = 0; + return data; +} + +/* */ +void capwap_acname_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_acname.h b/src/common/capwap_element_acname.h new file mode 100644 index 0000000..8bc73ee --- /dev/null +++ b/src/common/capwap_element_acname.h @@ -0,0 +1,24 @@ +#ifndef __CAPWAP_ELEMENT_ACNAME_HEADER__ +#define __CAPWAP_ELEMENT_ACNAME_HEADER__ + +#define CAPWAP_ELEMENT_ACNAME 4 + +#define CAPWAP_ACNAME_MAXLENGTH 512 + +struct capwap_acname_element { + char name[CAPWAP_ACNAME_MAXLENGTH + 1]; +}; + +struct capwap_message_element* capwap_acname_element_create(void* data, unsigned long datalength); +int capwap_acname_element_validate(struct capwap_message_element* element); +void* capwap_acname_element_parsing(struct capwap_message_element* element); +void capwap_acname_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_ACNAME_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_ACNAME); \ + f->create(x, sizeof(struct capwap_acname_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_ACNAME_HEADER__ */ diff --git a/src/common/capwap_element_acnamepriority.c b/src/common/capwap_element_acnamepriority.c new file mode 100644 index 0000000..bd6cc5d --- /dev/null +++ b/src/common/capwap_element_acnamepriority.c @@ -0,0 +1,91 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Priority | AC Name... ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 5 for AC Name with Priority +Length: >= 2 + +********************************************************************/ + +struct capwap_acnamepriority_raw_element { + unsigned char priority; + char name[0]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_acnamepriority_element_create(void* data, unsigned long datalength) { + unsigned short namelength; + struct capwap_message_element* element; + struct capwap_acnamepriority_raw_element* dataraw; + struct capwap_acnamepriority_element* dataelement = (struct capwap_acnamepriority_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_acnamepriority_element)); + + /* Alloc block of memory */ + namelength = strlen(dataelement->name); + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_acnamepriority_raw_element) + namelength); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_acnamepriority_raw_element) + namelength); + element->type = htons(CAPWAP_ELEMENT_ACNAMEPRIORITY); + element->length = htons(sizeof(struct capwap_acnamepriority_raw_element) + namelength); + + dataraw = (struct capwap_acnamepriority_raw_element*)element->data; + dataraw->priority = dataelement->priority; + memcpy(&dataraw->name[0], &dataelement->name[0], namelength); + + return element; +} + +/* */ +int capwap_acnamepriority_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_acnamepriority_element_parsing(struct capwap_message_element* element) { + unsigned short namelength; + struct capwap_acnamepriority_element* data; + struct capwap_acnamepriority_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_ACNAMEPRIORITY); + + namelength = ntohs(element->length) - sizeof(struct capwap_acnamepriority_raw_element); + if (!namelength || (namelength > CAPWAP_ACNAMEPRIORITY_MAXLENGTH)) { + return NULL; + } + + /* */ + dataraw = (struct capwap_acnamepriority_raw_element*)element->data; + data = (struct capwap_acnamepriority_element*)capwap_alloc(sizeof(struct capwap_acnamepriority_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->priority = dataraw->priority; + memcpy(&data->name[0], &dataraw->name[0], namelength); + data->name[namelength] = 0; + + return data; +} + +/* */ +void capwap_acnamepriority_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_acnamepriority.h b/src/common/capwap_element_acnamepriority.h new file mode 100644 index 0000000..cb34158 --- /dev/null +++ b/src/common/capwap_element_acnamepriority.h @@ -0,0 +1,25 @@ +#ifndef __CAPWAP_ELEMENT_ACNAMEPRIORITY_HEADER__ +#define __CAPWAP_ELEMENT_ACNAMEPRIORITY_HEADER__ + +#define CAPWAP_ELEMENT_ACNAMEPRIORITY 5 + +#define CAPWAP_ACNAMEPRIORITY_MAXLENGTH 512 + +struct capwap_acnamepriority_element { + unsigned char priority; + char name[CAPWAP_ACNAMEPRIORITY_MAXLENGTH + 1]; +}; + +struct capwap_message_element* capwap_acnamepriority_element_create(void* data, unsigned long datalength); +int capwap_acnamepriority_element_validate(struct capwap_message_element* element); +void* capwap_acnamepriority_element_parsing(struct capwap_message_element* element); +void capwap_acnamepriority_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_ACNAMEPRIORITY_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_ACNAMEPRIORITY); \ + f->create(x, sizeof(struct capwap_acnamepriority_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_ACNAMEPRIORITY_HEADER__ */ diff --git a/src/common/capwap_element_controlipv4.c b/src/common/capwap_element_controlipv4.c new file mode 100644 index 0000000..d72cfb9 --- /dev/null +++ b/src/common/capwap_element_controlipv4.c @@ -0,0 +1,88 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| WTP Count | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 10 for CAPWAP Control IPv4 Address +Length: 6 + +********************************************************************/ + +struct capwap_controlipv4_raw_element { + unsigned long address; + unsigned short wtpcount; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_controlipv4_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_controlipv4_element* dataelement = (struct capwap_controlipv4_element*)data; + struct capwap_controlipv4_raw_element* dataraw; + + ASSERT(data != NULL); + ASSERT(datalength >= sizeof(struct capwap_controlipv4_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_controlipv4_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_controlipv4_raw_element)); + element->type = htons(CAPWAP_ELEMENT_CONTROLIPV4); + element->length = htons(sizeof(struct capwap_controlipv4_raw_element)); + + dataraw = (struct capwap_controlipv4_raw_element*)element->data; + dataraw->address = dataelement->address.s_addr; + dataraw->wtpcount = htons(dataelement->wtpcount); + + return element; +} + +/* */ +int capwap_controlipv4_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_controlipv4_element_parsing(struct capwap_message_element* element) { + struct capwap_controlipv4_element* data; + struct capwap_controlipv4_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_CONTROLIPV4); + + if (ntohs(element->length) != sizeof(struct capwap_controlipv4_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_controlipv4_element*)capwap_alloc(sizeof(struct capwap_controlipv4_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + dataraw = (struct capwap_controlipv4_raw_element*)element->data; + data->address.s_addr = dataraw->address; + data->wtpcount = ntohs(dataraw->wtpcount); + + return data; +} + +/* */ +void capwap_controlipv4_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_controlipv4.h b/src/common/capwap_element_controlipv4.h new file mode 100644 index 0000000..fbb7ebb --- /dev/null +++ b/src/common/capwap_element_controlipv4.h @@ -0,0 +1,23 @@ +#ifndef __CAPWAP_ELEMENT_CONTROLIPV4_HEADER__ +#define __CAPWAP_ELEMENT_CONTROLIPV4_HEADER__ + +#define CAPWAP_ELEMENT_CONTROLIPV4 10 + +struct capwap_controlipv4_element { + struct in_addr address; + unsigned short wtpcount; +}; + +struct capwap_message_element* capwap_controlipv4_element_create(void* data, unsigned long datalength); +int capwap_controlipv4_element_validate(struct capwap_message_element* element); +void* capwap_controlipv4_element_parsing(struct capwap_message_element* element); +void capwap_controlipv4_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_CONTROLIPV4_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_CONTROLIPV4); \ + f->create(x, sizeof(struct capwap_controlipv4_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_CONTROLIPV4_HEADER__ */ diff --git a/src/common/capwap_element_controlipv6.c b/src/common/capwap_element_controlipv6.c new file mode 100644 index 0000000..6211bc2 --- /dev/null +++ b/src/common/capwap_element_controlipv6.c @@ -0,0 +1,94 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| WTP Count | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 11 for CAPWAP Control IPv6 Address +Length: 18 + +********************************************************************/ + +struct capwap_controlipv6_raw_element { + unsigned long address[4]; + unsigned short wtpcount; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_controlipv6_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_controlipv6_element* dataelement = (struct capwap_controlipv6_element*)data; + struct capwap_controlipv6_raw_element* dataraw; + + ASSERT(data != NULL); + ASSERT(datalength >= sizeof(struct capwap_controlipv6_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_controlipv6_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_controlipv6_raw_element)); + element->type = htons(CAPWAP_ELEMENT_CONTROLIPV6); + element->length = htons(sizeof(struct capwap_controlipv6_raw_element)); + + dataraw = (struct capwap_controlipv6_raw_element*)element->data; + memcpy(dataraw->address, dataelement->address.s6_addr32, sizeof(unsigned long) * 4); + dataraw->wtpcount = htons(dataelement->wtpcount); + + return element; +} + +/* */ +int capwap_controlipv6_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_controlipv6_element_parsing(struct capwap_message_element* element) { + struct capwap_controlipv6_element* data; + struct capwap_controlipv6_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_CONTROLIPV6); + + if (ntohs(element->length) != sizeof(struct capwap_controlipv6_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_controlipv6_element*)capwap_alloc(sizeof(struct capwap_controlipv6_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + dataraw = (struct capwap_controlipv6_raw_element*)element->data; + memcpy(data->address.s6_addr32, dataraw->address, sizeof(unsigned long) * 4); + data->wtpcount = ntohs(dataraw->wtpcount); + + return data; +} + +/* */ +void capwap_controlipv6_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_controlipv6.h b/src/common/capwap_element_controlipv6.h new file mode 100644 index 0000000..2963eb6 --- /dev/null +++ b/src/common/capwap_element_controlipv6.h @@ -0,0 +1,23 @@ +#ifndef __CAPWAP_ELEMENT_CONTROLIPV6_HEADER__ +#define __CAPWAP_ELEMENT_CONTROLIPV6_HEADER__ + +#define CAPWAP_ELEMENT_CONTROLIPV6 11 + +struct capwap_controlipv6_element { + struct in6_addr address; + unsigned short wtpcount; +}; + +struct capwap_message_element* capwap_controlipv6_element_create(void* data, unsigned long datalength); +int capwap_controlipv6_element_validate(struct capwap_message_element* element); +void* capwap_controlipv6_element_parsing(struct capwap_message_element* element); +void capwap_controlipv6_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_CONTROLIPV6_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_CONTROLIPV6); \ + f->create(x, sizeof(struct capwap_controlipv6_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_CONTROLIPV4_HEADER__ */ diff --git a/src/common/capwap_element_decrypterrorreportperiod.c b/src/common/capwap_element_decrypterrorreportperiod.c new file mode 100644 index 0000000..e729ce0 --- /dev/null +++ b/src/common/capwap_element_decrypterrorreportperiod.c @@ -0,0 +1,86 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Radio ID | Report Interval | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 16 for Decryption Error Report Period +Length: 3 + +********************************************************************/ + +struct capwap_decrypterrorreportperiod_raw_element { + unsigned char radioid; + unsigned short interval; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_decrypterrorreportperiod_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_decrypterrorreportperiod_element* dataelement = (struct capwap_decrypterrorreportperiod_element*)data; + struct capwap_decrypterrorreportperiod_raw_element* dataraw; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_decrypterrorreportperiod_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_decrypterrorreportperiod_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_decrypterrorreportperiod_raw_element)); + element->type = htons(CAPWAP_ELEMENT_DECRYPTERRORREPORTPERIOD); + element->length = htons(sizeof(struct capwap_decrypterrorreportperiod_raw_element)); + + dataraw = (struct capwap_decrypterrorreportperiod_raw_element*)element->data; + dataraw->radioid = dataelement->radioid; + dataraw->interval = htons(dataelement->interval); + + return element; +} + +/* */ +int capwap_decrypterrorreportperiod_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_decrypterrorreportperiod_element_parsing(struct capwap_message_element* element) { + struct capwap_decrypterrorreportperiod_element* data; + struct capwap_decrypterrorreportperiod_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_DECRYPTERRORREPORTPERIOD); + + if (ntohs(element->length) != sizeof(struct capwap_decrypterrorreportperiod_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_decrypterrorreportperiod_element*)capwap_alloc(sizeof(struct capwap_decrypterrorreportperiod_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + dataraw = (struct capwap_decrypterrorreportperiod_raw_element*)element->data; + data->radioid = dataraw->radioid; + data->interval = ntohs(dataraw->interval); + + return data; +} + +/* */ +void capwap_decrypterrorreportperiod_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_decrypterrorreportperiod.h b/src/common/capwap_element_decrypterrorreportperiod.h new file mode 100644 index 0000000..e479175 --- /dev/null +++ b/src/common/capwap_element_decrypterrorreportperiod.h @@ -0,0 +1,22 @@ +#ifndef __CAPWAP_ELEMENT_DECRYPTERRORREPORTPERIOD_HEADER__ +#define __CAPWAP_ELEMENT_DECRYPTERRORREPORTPERIOD_HEADER__ + +#define CAPWAP_ELEMENT_DECRYPTERRORREPORTPERIOD 16 + +struct capwap_decrypterrorreportperiod_element { + unsigned char radioid; + unsigned short interval; +}; + +struct capwap_message_element* capwap_decrypterrorreportperiod_element_create(void* data, unsigned long length); +int capwap_decrypterrorreportperiod_element_validate(struct capwap_message_element* element); +void* capwap_decrypterrorreportperiod_element_parsing(struct capwap_message_element* element); +void capwap_decrypterrorreportperiod_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_DECRYPTERRORREPORTPERIOD_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_DECRYPTERRORREPORTPERIOD); \ + f->create(x, sizeof(struct capwap_decrypterrorreportperiod_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_DECRYPTERRORREPORTPERIOD_HEADER__ */ diff --git a/src/common/capwap_element_discoverytype.c b/src/common/capwap_element_discoverytype.c new file mode 100644 index 0000000..b684c90 --- /dev/null +++ b/src/common/capwap_element_discoverytype.c @@ -0,0 +1,75 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 + 0 1 2 3 4 5 6 7 ++-+-+-+-+-+-+-+-+ +| Discovery Type| ++-+-+-+-+-+-+-+-+ + +Type: 20 for Discovery Type +Length: 1 + +********************************************************************/ + +struct capwap_discoverytype_raw_element { + unsigned char type; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_discoverytype_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + + ASSERT(data != NULL); + ASSERT(datalength >= sizeof(struct capwap_discoverytype_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_discoverytype_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_discoverytype_raw_element)); + element->type = htons(CAPWAP_ELEMENT_DISCOVERYTYPE); + element->length = htons(sizeof(struct capwap_discoverytype_raw_element)); + ((struct capwap_discoverytype_raw_element*)element->data)->type = ((struct capwap_discoverytype_element*)data)->type; + return element; +} + +/* */ +int capwap_discoverytype_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_discoverytype_element_parsing(struct capwap_message_element* element) { + struct capwap_discoverytype_element* data; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_DISCOVERYTYPE); + + if (ntohs(element->length) != sizeof(struct capwap_discoverytype_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_discoverytype_element*)capwap_alloc(sizeof(struct capwap_discoverytype_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->type = ((struct capwap_discoverytype_raw_element*)element->data)->type; + return data; +} + +/* */ +void capwap_discoverytype_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_discoverytype.h b/src/common/capwap_element_discoverytype.h new file mode 100644 index 0000000..c404b42 --- /dev/null +++ b/src/common/capwap_element_discoverytype.h @@ -0,0 +1,28 @@ +#ifndef __CAPWAP_ELEMENT_DISCOVERYTYPE_HEADER__ +#define __CAPWAP_ELEMENT_DISCOVERYTYPE_HEADER__ + +#define CAPWAP_ELEMENT_DISCOVERYTYPE 20 + +#define CAPWAP_ELEMENT_DISCOVERYTYPE_TYPE_UNKNOWN 0 +#define CAPWAP_ELEMENT_DISCOVERYTYPE_TYPE_STATIC 1 +#define CAPWAP_ELEMENT_DISCOVERYTYPE_TYPE_DHCP 2 +#define CAPWAP_ELEMENT_DISCOVERYTYPE_TYPE_DNS 3 +#define CAPWAP_ELEMENT_DISCOVERYTYPE_TYPE_ACREFERRAL 4 + +struct capwap_discoverytype_element { + unsigned char type; +}; + +struct capwap_message_element* capwap_discoverytype_element_create(void* data, unsigned long datalength); +int capwap_discoverytype_element_validate(struct capwap_message_element* element); +void* capwap_discoverytype_element_parsing(struct capwap_message_element* element); +void capwap_discoverytype_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_DISCOVERYTYPE_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_DISCOVERYTYPE); \ + f->create(x, sizeof(struct capwap_discoverytype_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_DISCOVERYTYPE_HEADER__ */ diff --git a/src/common/capwap_element_ecnsupport.c b/src/common/capwap_element_ecnsupport.c new file mode 100644 index 0000000..5b86816 --- /dev/null +++ b/src/common/capwap_element_ecnsupport.c @@ -0,0 +1,77 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 + 0 1 2 3 4 5 6 7 ++-+-+-+-+-+-+-+-+ +| ECN Support | ++-+-+-+-+-+-+-+-+ + +Type: 53 for ECN Support +Length: 1 + +********************************************************************/ + +struct capwap_ecnsupport_raw_element { + char flag; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_ecnsupport_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_ecnsupport_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_ecnsupport_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_ecnsupport_raw_element)); + element->type = htons(CAPWAP_ELEMENT_ECNSUPPORT); + element->length = htons(sizeof(struct capwap_ecnsupport_raw_element)); + + ((struct capwap_ecnsupport_raw_element*)element->data)->flag = ((struct capwap_ecnsupport_element*)data)->flag; + + return element; +} + +/* */ +int capwap_ecnsupport_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_ecnsupport_element_parsing(struct capwap_message_element* element) { + struct capwap_ecnsupport_element* data; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_ECNSUPPORT); + + if (ntohs(element->length) != sizeof(struct capwap_ecnsupport_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_ecnsupport_element*)capwap_alloc(sizeof(struct capwap_ecnsupport_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->flag = ((struct capwap_ecnsupport_raw_element*)element->data)->flag; + return data; +} + +/* */ +void capwap_ecnsupport_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_ecnsupport.h b/src/common/capwap_element_ecnsupport.h new file mode 100644 index 0000000..bc4ff79 --- /dev/null +++ b/src/common/capwap_element_ecnsupport.h @@ -0,0 +1,24 @@ +#ifndef __CAPWAP_ELEMENT_ECNSUPPORT_HEADER__ +#define __CAPWAP_ELEMENT_ECNSUPPORT_HEADER__ + +#define CAPWAP_ELEMENT_ECNSUPPORT 53 + +struct capwap_ecnsupport_element { + char flag; +}; + +#define CAPWAP_LIMITED_ECN_SUPPORT 0 +#define CAPWAP_FULL_ECN_SUPPORT 1 + +struct capwap_message_element* capwap_ecnsupport_element_create(void* data, unsigned long length); +int capwap_ecnsupport_element_validate(struct capwap_message_element* element); +void* capwap_ecnsupport_element_parsing(struct capwap_message_element* element); +void capwap_ecnsupport_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_ECNSUPPORT_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_ECNSUPPORT); \ + f->create(x, sizeof(struct capwap_ecnsupport_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_ECNSUPPORT_HEADER__ */ diff --git a/src/common/capwap_element_idletimeout.c b/src/common/capwap_element_idletimeout.c new file mode 100644 index 0000000..ee4e30b --- /dev/null +++ b/src/common/capwap_element_idletimeout.c @@ -0,0 +1,77 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Timeout | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 23 for Idle Timeout +Length: 4 + +********************************************************************/ + +struct capwap_idletimeout_raw_element { + unsigned long timeout; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_idletimeout_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_idletimeout_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_idletimeout_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_idletimeout_raw_element)); + element->type = htons(CAPWAP_ELEMENT_IDLETIMEOUT); + element->length = htons(sizeof(struct capwap_idletimeout_raw_element)); + + ((struct capwap_idletimeout_raw_element*)element->data)->timeout = htonl(((struct capwap_idletimeout_element*)data)->timeout); + + return element; +} + +/* */ +int capwap_idletimeout_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_idletimeout_element_parsing(struct capwap_message_element* element) { + struct capwap_idletimeout_element* data; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_IDLETIMEOUT); + + if (ntohs(element->length) != sizeof(struct capwap_idletimeout_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_idletimeout_element*)capwap_alloc(sizeof(struct capwap_idletimeout_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->timeout = ntohl(((struct capwap_idletimeout_raw_element*)element->data)->timeout); + return data; +} + +/* */ +void capwap_idletimeout_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_idletimeout.h b/src/common/capwap_element_idletimeout.h new file mode 100644 index 0000000..e640954 --- /dev/null +++ b/src/common/capwap_element_idletimeout.h @@ -0,0 +1,21 @@ +#ifndef __CAPWAP_ELEMENT_IDLETIMEOUT_HEADER__ +#define __CAPWAP_ELEMENT_IDLETIMEOUT_HEADER__ + +#define CAPWAP_ELEMENT_IDLETIMEOUT 23 + +struct capwap_idletimeout_element { + unsigned long timeout; +}; + +struct capwap_message_element* capwap_idletimeout_element_create(void* data, unsigned long length); +int capwap_idletimeout_element_validate(struct capwap_message_element* element); +void* capwap_idletimeout_element_parsing(struct capwap_message_element* element); +void capwap_idletimeout_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_IDLETIMEOUT_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_IDLETIMEOUT); \ + f->create(x, sizeof(struct capwap_idletimeout_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_IDLETIMEOUT_HEADER__ */ diff --git a/src/common/capwap_element_imageidentifier.c b/src/common/capwap_element_imageidentifier.c new file mode 100644 index 0000000..89d48c6 --- /dev/null +++ b/src/common/capwap_element_imageidentifier.c @@ -0,0 +1,93 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Vendor Identifier | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Data... ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 25 for Image Identifier +Length: >= 5 + +********************************************************************/ + +struct capwap_imageidentifier_raw_element { + unsigned long vendor; + char name[0]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_imageidentifier_element_create(void* data, unsigned long datalength) { + unsigned short namelength; + struct capwap_message_element* element; + struct capwap_imageidentifier_raw_element* dataraw; + struct capwap_imageidentifier_element* dataelement = (struct capwap_imageidentifier_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_imageidentifier_element)); + + /* Alloc block of memory */ + namelength = strlen(dataelement->name); + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_imageidentifier_raw_element) + namelength); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_imageidentifier_raw_element) + namelength); + element->type = htons(CAPWAP_ELEMENT_IMAGEIDENTIFIER); + element->length = htons(sizeof(struct capwap_imageidentifier_raw_element) + namelength); + + dataraw = (struct capwap_imageidentifier_raw_element*)element->data; + dataraw->vendor = htonl(dataelement->vendor); + memcpy(&dataraw->name[0], &dataelement->name[0], namelength); + + return element; +} + +/* */ +int capwap_imageidentifier_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_imageidentifier_element_parsing(struct capwap_message_element* element) { + unsigned short namelength; + struct capwap_imageidentifier_element* data; + struct capwap_imageidentifier_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_IMAGEIDENTIFIER); + + namelength = ntohs(element->length) - sizeof(struct capwap_imageidentifier_raw_element); + if (!namelength || (namelength > CAPWAP_IMAGEIDENTIFIER_MAXLENGTH)) { + return NULL; + } + + /* */ + dataraw = (struct capwap_imageidentifier_raw_element*)element->data; + data = (struct capwap_imageidentifier_element*)capwap_alloc(sizeof(struct capwap_imageidentifier_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->vendor = ntohl(dataraw->vendor); + memcpy(&data->name[0], &dataraw->name[0], namelength); + data->name[namelength] = 0; + + return data; +} + +/* */ +void capwap_imageidentifier_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_imageidentifier.h b/src/common/capwap_element_imageidentifier.h new file mode 100644 index 0000000..c0d8e4d --- /dev/null +++ b/src/common/capwap_element_imageidentifier.h @@ -0,0 +1,25 @@ +#ifndef __CAPWAP_ELEMENT_IMAGEIDENTIFIER_HEADER__ +#define __CAPWAP_ELEMENT_IMAGEIDENTIFIER_HEADER__ + +#define CAPWAP_ELEMENT_IMAGEIDENTIFIER 25 + +#define CAPWAP_IMAGEIDENTIFIER_MAXLENGTH 1024 + +struct capwap_imageidentifier_element { + unsigned long vendor; + char name[CAPWAP_IMAGEIDENTIFIER_MAXLENGTH + 1]; +}; + +struct capwap_message_element* capwap_imageidentifier_element_create(void* data, unsigned long datalength); +int capwap_imageidentifier_element_validate(struct capwap_message_element* element); +void* capwap_imageidentifier_element_parsing(struct capwap_message_element* element); +void capwap_imageidentifier_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_IMAGEIDENTIFIER_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_IMAGEIDENTIFIER); \ + f->create(x, sizeof(struct capwap_imageidentifier_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_IMAGEIDENTIFIER_HEADER__ */ diff --git a/src/common/capwap_element_localipv4.c b/src/common/capwap_element_localipv4.c new file mode 100644 index 0000000..53f9231 --- /dev/null +++ b/src/common/capwap_element_localipv4.c @@ -0,0 +1,83 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 30 for CAPWAP Local IPv4 Address +Length: 4 + +********************************************************************/ + +struct capwap_localipv4_raw_element { + unsigned long address; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_localipv4_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_localipv4_element* dataelement = (struct capwap_localipv4_element*)data; + struct capwap_localipv4_raw_element* dataraw; + + ASSERT(data != NULL); + ASSERT(datalength >= sizeof(struct capwap_localipv4_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_localipv4_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_localipv4_raw_element)); + element->type = htons(CAPWAP_ELEMENT_LOCALIPV4); + element->length = htons(sizeof(struct capwap_localipv4_raw_element)); + + dataraw = (struct capwap_localipv4_raw_element*)element->data; + dataraw->address = dataelement->address.s_addr; + + return element; +} + +/* */ +int capwap_localipv4_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_localipv4_element_parsing(struct capwap_message_element* element) { + struct capwap_localipv4_element* data; + struct capwap_localipv4_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_LOCALIPV4); + + if (ntohs(element->length) != sizeof(struct capwap_localipv4_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_localipv4_element*)capwap_alloc(sizeof(struct capwap_localipv4_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + dataraw = (struct capwap_localipv4_raw_element*)element->data; + data->address.s_addr = dataraw->address; + + return data; +} + +/* */ +void capwap_localipv4_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_localipv4.h b/src/common/capwap_element_localipv4.h new file mode 100644 index 0000000..ac0c17f --- /dev/null +++ b/src/common/capwap_element_localipv4.h @@ -0,0 +1,22 @@ +#ifndef __CAPWAP_ELEMENT_LOCALIPV4_HEADER__ +#define __CAPWAP_ELEMENT_LOCALIPV4_HEADER__ + +#define CAPWAP_ELEMENT_LOCALIPV4 30 + +struct capwap_localipv4_element { + struct in_addr address; +}; + +struct capwap_message_element* capwap_localipv4_element_create(void* data, unsigned long datalength); +int capwap_localipv4_element_validate(struct capwap_message_element* element); +void* capwap_localipv4_element_parsing(struct capwap_message_element* element); +void capwap_localipv4_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_LOCALIPV4_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_LOCALIPV4); \ + f->create(x, sizeof(struct capwap_localipv4_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_LOCALIPV4_HEADER__ */ diff --git a/src/common/capwap_element_localipv6.c b/src/common/capwap_element_localipv6.c new file mode 100644 index 0000000..dbeaf96 --- /dev/null +++ b/src/common/capwap_element_localipv6.c @@ -0,0 +1,89 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 50 for CAPWAP Local IPv6 Address +Length: 16 + +********************************************************************/ + +struct capwap_localipv6_raw_element { + unsigned long address[4]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_localipv6_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_localipv6_element* dataelement = (struct capwap_localipv6_element*)data; + struct capwap_localipv6_raw_element* dataraw; + + ASSERT(data != NULL); + ASSERT(datalength >= sizeof(struct capwap_localipv6_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_localipv6_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_localipv6_raw_element)); + element->type = htons(CAPWAP_ELEMENT_LOCALIPV6); + element->length = htons(sizeof(struct capwap_localipv6_raw_element)); + + dataraw = (struct capwap_localipv6_raw_element*)element->data; + memcpy(dataraw->address, dataelement->address.s6_addr32, sizeof(unsigned long) * 4); + + return element; +} + +/* */ +int capwap_localipv6_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_localipv6_element_parsing(struct capwap_message_element* element) { + struct capwap_localipv6_element* data; + struct capwap_localipv6_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_LOCALIPV6); + + if (ntohs(element->length) != sizeof(struct capwap_localipv6_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_localipv6_element*)capwap_alloc(sizeof(struct capwap_localipv6_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + dataraw = (struct capwap_localipv6_raw_element*)element->data; + memcpy(data->address.s6_addr32, dataraw->address, sizeof(unsigned long) * 4); + + return data; +} + +/* */ +void capwap_localipv6_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_localipv6.h b/src/common/capwap_element_localipv6.h new file mode 100644 index 0000000..561c02c --- /dev/null +++ b/src/common/capwap_element_localipv6.h @@ -0,0 +1,22 @@ +#ifndef __CAPWAP_ELEMENT_LOCALIPV6_HEADER__ +#define __CAPWAP_ELEMENT_LOCALIPV6_HEADER__ + +#define CAPWAP_ELEMENT_LOCALIPV6 50 + +struct capwap_localipv6_element { + struct in6_addr address; +}; + +struct capwap_message_element* capwap_localipv6_element_create(void* data, unsigned long datalength); +int capwap_localipv6_element_validate(struct capwap_message_element* element); +void* capwap_localipv6_element_parsing(struct capwap_message_element* element); +void capwap_localipv6_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_LOCALIPV6_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_LOCALIPV6); \ + f->create(x, sizeof(struct capwap_localipv6_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_CONTROLIPV4_HEADER__ */ diff --git a/src/common/capwap_element_location.c b/src/common/capwap_element_location.c new file mode 100644 index 0000000..77c911a --- /dev/null +++ b/src/common/capwap_element_location.c @@ -0,0 +1,86 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 + 0 1 2 3 4 5 6 7 ++-+-+-+-+-+-+-+-+ +| Location ... ++-+-+-+-+-+-+-+-+ + +Type: 28 for Location Data +Length: >= 1 + +********************************************************************/ + +struct capwap_location_raw_element { + char value[0]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_location_element_create(void* data, unsigned long datalength) { + unsigned short namelength; + struct capwap_message_element* element; + struct capwap_location_raw_element* dataraw; + struct capwap_location_element* dataelement = (struct capwap_location_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength >= sizeof(struct capwap_location_element)); + + /* Alloc block of memory */ + namelength = strlen(dataelement->value); + element = capwap_alloc(sizeof(struct capwap_message_element) + namelength); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + namelength); + element->type = htons(CAPWAP_ELEMENT_LOCATION); + element->length = htons(namelength); + + dataraw = (struct capwap_location_raw_element*)element->data; + memcpy(&dataraw->value[0], &dataelement->value[0], namelength); + return element; +} + +/* */ +int capwap_location_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_location_element_parsing(struct capwap_message_element* element) { + unsigned short namelength; + struct capwap_location_element* data; + struct capwap_location_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_LOCATION); + + namelength = ntohs(element->length); + if (!namelength || (namelength > CAPWAP_LOCATION_MAXLENGTH)) { + return NULL; + } + + /* */ + dataraw = (struct capwap_location_raw_element*)element->data; + data = (struct capwap_location_element*)capwap_alloc(sizeof(struct capwap_location_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + memcpy(&data->value[0], &dataraw->value[0], namelength); + data->value[namelength] = 0; + return data; +} + +/* */ +void capwap_location_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_location.h b/src/common/capwap_element_location.h new file mode 100644 index 0000000..28bb516 --- /dev/null +++ b/src/common/capwap_element_location.h @@ -0,0 +1,24 @@ +#ifndef __CAPWAP_ELEMENT_LOCATION_HEADER__ +#define __CAPWAP_ELEMENT_LOCATION_HEADER__ + +#define CAPWAP_ELEMENT_LOCATION 28 + +#define CAPWAP_LOCATION_MAXLENGTH 1024 + +struct capwap_location_element { + char value[CAPWAP_LOCATION_MAXLENGTH + 1]; +}; + +struct capwap_message_element* capwap_location_element_create(void* data, unsigned long datalength); +int capwap_location_element_validate(struct capwap_message_element* element); +void* capwap_location_element_parsing(struct capwap_message_element* element); +void capwap_location_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_LOCATION_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_LOCATION); \ + f->create(x, sizeof(struct capwap_location_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_LOCATION_HEADER__ */ diff --git a/src/common/capwap_element_maximumlength.c b/src/common/capwap_element_maximumlength.c new file mode 100644 index 0000000..499aed2 --- /dev/null +++ b/src/common/capwap_element_maximumlength.c @@ -0,0 +1,77 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Maximum Message Length | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 29 for Maximum Message Length +Length: 2 + +********************************************************************/ + +struct capwap_maximumlength_raw_element { + unsigned short length; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_maximumlength_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_maximumlength_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_maximumlength_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_maximumlength_raw_element)); + element->type = htons(CAPWAP_ELEMENT_MAXIMUMLENGTH); + element->length = htons(sizeof(struct capwap_maximumlength_raw_element)); + + ((struct capwap_maximumlength_raw_element*)element->data)->length = htons(((struct capwap_maximumlength_element*)data)->length); + + return element; +} + +/* */ +int capwap_maximumlength_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_maximumlength_element_parsing(struct capwap_message_element* element) { + struct capwap_maximumlength_element* data; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_MAXIMUMLENGTH); + + if (ntohs(element->length) != sizeof(struct capwap_maximumlength_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_maximumlength_element*)capwap_alloc(sizeof(struct capwap_maximumlength_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->length = ntohs(((struct capwap_maximumlength_raw_element*)element->data)->length); + return data; +} + +/* */ +void capwap_maximumlength_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_maximumlength.h b/src/common/capwap_element_maximumlength.h new file mode 100644 index 0000000..6d0f84b --- /dev/null +++ b/src/common/capwap_element_maximumlength.h @@ -0,0 +1,21 @@ +#ifndef __CAPWAP_ELEMENT_MAXIMUMLENGTH_HEADER__ +#define __CAPWAP_ELEMENT_MAXIMUMLENGTH_HEADER__ + +#define CAPWAP_ELEMENT_MAXIMUMLENGTH 29 + +struct capwap_maximumlength_element { + unsigned short length; +}; + +struct capwap_message_element* capwap_maximumlength_element_create(void* data, unsigned long length); +int capwap_maximumlength_element_validate(struct capwap_message_element* element); +void* capwap_maximumlength_element_parsing(struct capwap_message_element* element); +void capwap_maximumlength_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_MAXIMUMLENGTH_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_MAXIMUMLENGTH); \ + f->create(x, sizeof(struct capwap_maximumlength_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_MAXIMUMLENGTH_HEADER__ */ diff --git a/src/common/capwap_element_mtudiscovery.c b/src/common/capwap_element_mtudiscovery.c new file mode 100644 index 0000000..712eeba --- /dev/null +++ b/src/common/capwap_element_mtudiscovery.c @@ -0,0 +1,72 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 + 0 1 2 3 4 5 6 7 ++-+-+-+-+-+-+-+-+ +| Padding... ++-+-+-+-+-+-+-+- + +Type: 52 for MTU Discovery Padding +Length: variable + +********************************************************************/ + +/* */ +struct capwap_message_element* capwap_mtudiscovery_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_mtudiscovery_element* dataelement = (struct capwap_mtudiscovery_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_mtudiscovery_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + dataelement->length); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element)); + element->type = htons(CAPWAP_ELEMENT_MTUDISCOVERY); + element->length = htons(dataelement->length); + + if (dataelement->length > 0) { + memset(element->data, 0xff, dataelement->length); + } + + return element; +} + +/* */ +int capwap_mtudiscovery_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_mtudiscovery_element_parsing(struct capwap_message_element* element) { + struct capwap_mtudiscovery_element* data; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_MTUDISCOVERY); + + /* */ + data = (struct capwap_mtudiscovery_element*)capwap_alloc(sizeof(struct capwap_mtudiscovery_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->length = ntohs(element->length); + return data; +} + +/* */ +void capwap_mtudiscovery_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_mtudiscovery.h b/src/common/capwap_element_mtudiscovery.h new file mode 100644 index 0000000..706e7e5 --- /dev/null +++ b/src/common/capwap_element_mtudiscovery.h @@ -0,0 +1,21 @@ +#ifndef __CAPWAP_ELEMENT_MTUDISCOVERY_HEADER__ +#define __CAPWAP_ELEMENT_MTUDISCOVERY_HEADER__ + +#define CAPWAP_ELEMENT_MTUDISCOVERY 52 + +struct capwap_mtudiscovery_element { + unsigned short length; +}; + +struct capwap_message_element* capwap_mtudiscovery_element_create(void* data, unsigned long length); +int capwap_mtudiscovery_element_validate(struct capwap_message_element* element); +void* capwap_mtudiscovery_element_parsing(struct capwap_message_element* element); +void capwap_mtudiscovery_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_MTUDISCOVERY_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_MTUDISCOVERY); \ + f->create(x, sizeof(struct capwap_mtudiscovery_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_MTUDISCOVERY_HEADER__ */ diff --git a/src/common/capwap_element_radioadmstate.c b/src/common/capwap_element_radioadmstate.c new file mode 100644 index 0000000..dee558d --- /dev/null +++ b/src/common/capwap_element_radioadmstate.c @@ -0,0 +1,86 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Radio ID | Admin State | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 31 for Radio Administrative State +Length: 2 + +********************************************************************/ + +struct capwap_radioadmstate_raw_element { + unsigned char radioid; + unsigned char state; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_radioadmstate_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_radioadmstate_element* dataelement = (struct capwap_radioadmstate_element*)data; + struct capwap_radioadmstate_raw_element* dataraw; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_radioadmstate_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_radioadmstate_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_radioadmstate_raw_element)); + element->type = htons(CAPWAP_ELEMENT_RADIOADMSTATE); + element->length = htons(sizeof(struct capwap_radioadmstate_raw_element)); + + dataraw = (struct capwap_radioadmstate_raw_element*)element->data; + dataraw->radioid = dataelement->radioid; + dataraw->state = dataelement->state; + + return element; +} + +/* */ +int capwap_radioadmstate_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_radioadmstate_element_parsing(struct capwap_message_element* element) { + struct capwap_radioadmstate_element* data; + struct capwap_radioadmstate_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_RADIOADMSTATE); + + if (ntohs(element->length) != sizeof(struct capwap_radioadmstate_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_radioadmstate_element*)capwap_alloc(sizeof(struct capwap_radioadmstate_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + dataraw = (struct capwap_radioadmstate_raw_element*)element->data; + data->radioid = dataraw->radioid; + data->state = dataraw->state; + + return data; +} + +/* */ +void capwap_radioadmstate_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_radioadmstate.h b/src/common/capwap_element_radioadmstate.h new file mode 100644 index 0000000..a361e80 --- /dev/null +++ b/src/common/capwap_element_radioadmstate.h @@ -0,0 +1,25 @@ +#ifndef __CAPWAP_ELEMENT_RADIOADMSTATE_HEADER__ +#define __CAPWAP_ELEMENT_RADIOADMSTATE_HEADER__ + +#define CAPWAP_ELEMENT_RADIOADMSTATE 31 + +struct capwap_radioadmstate_element { + unsigned char radioid; + unsigned char state; +}; + +#define CAPWAP_RADIO_ADMIN_STATE_ENABLED 1 +#define CAPWAP_RADIO_ADMIN_STATE_DISABLED 2 + +struct capwap_message_element* capwap_radioadmstate_element_create(void* data, unsigned long length); +int capwap_radioadmstate_element_validate(struct capwap_message_element* element); +void* capwap_radioadmstate_element_parsing(struct capwap_message_element* element); +void capwap_radioadmstate_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_RADIOADMSTATE_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_RADIOADMSTATE); \ + f->create(x, sizeof(struct capwap_radioadmstate_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_RADIOADMSTATE_HEADER__ */ diff --git a/src/common/capwap_element_radiooprstate.c b/src/common/capwap_element_radiooprstate.c new file mode 100644 index 0000000..a7ceab6 --- /dev/null +++ b/src/common/capwap_element_radiooprstate.c @@ -0,0 +1,89 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Radio ID | State | Cause | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 32 for Radio Operational State +Length: 3 + +********************************************************************/ + +struct capwap_radiooprstate_raw_element { + unsigned char radioid; + unsigned char state; + unsigned char cause; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_radiooprstate_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_radiooprstate_element* dataelement = (struct capwap_radiooprstate_element*)data; + struct capwap_radiooprstate_raw_element* dataraw; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_radiooprstate_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_radiooprstate_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_radiooprstate_raw_element)); + element->type = htons(CAPWAP_ELEMENT_RADIOOPRSTATE); + element->length = htons(sizeof(struct capwap_radiooprstate_raw_element)); + + dataraw = (struct capwap_radiooprstate_raw_element*)element->data; + dataraw->radioid = dataelement->radioid; + dataraw->state = dataelement->state; + dataraw->cause = dataelement->cause; + + return element; +} + +/* */ +int capwap_radiooprstate_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_radiooprstate_element_parsing(struct capwap_message_element* element) { + struct capwap_radiooprstate_element* data; + struct capwap_radiooprstate_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_RADIOOPRSTATE); + + if (ntohs(element->length) != sizeof(struct capwap_radiooprstate_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_radiooprstate_element*)capwap_alloc(sizeof(struct capwap_radiooprstate_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + dataraw = (struct capwap_radiooprstate_raw_element*)element->data; + data->radioid = dataraw->radioid; + data->state = dataraw->state; + data->cause = dataraw->cause; + + return data; +} + +/* */ +void capwap_radiooprstate_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_radiooprstate.h b/src/common/capwap_element_radiooprstate.h new file mode 100644 index 0000000..7cea6b0 --- /dev/null +++ b/src/common/capwap_element_radiooprstate.h @@ -0,0 +1,31 @@ +#ifndef __CAPWAP_ELEMENT_RADIOOPRSTATE_HEADER__ +#define __CAPWAP_ELEMENT_RADIOOPRSTATE_HEADER__ + +#define CAPWAP_ELEMENT_RADIOOPRSTATE 32 + +struct capwap_radiooprstate_element { + unsigned char radioid; + unsigned char state; + unsigned char cause; +}; + +#define CAPWAP_RADIO_OPERATIONAL_STATE_ENABLED 1 +#define CAPWAP_RADIO_OPERATIONAL_STATE_DISABLED 2 + +#define CAPWAP_RADIO_OPERATIONAL_CAUSE_NORMAL 0 +#define CAPWAP_RADIO_OPERATIONAL_CAUSE_RADIOFAILURE 1 +#define CAPWAP_RADIO_OPERATIONAL_CAUSE_SOFTWAREFAILURE 2 +#define CAPWAP_RADIO_OPERATIONAL_CAUSE_ADMINSET 3 + +struct capwap_message_element* capwap_radiooprstate_element_create(void* data, unsigned long length); +int capwap_radiooprstate_element_validate(struct capwap_message_element* element); +void* capwap_radiooprstate_element_parsing(struct capwap_message_element* element); +void capwap_radiooprstate_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_RADIOOPRSTATE_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_RADIOOPRSTATE); \ + f->create(x, sizeof(struct capwap_radiooprstate_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_RADIOOPRSTATE_HEADER__ */ diff --git a/src/common/capwap_element_resultcode.c b/src/common/capwap_element_resultcode.c new file mode 100644 index 0000000..614ed6b --- /dev/null +++ b/src/common/capwap_element_resultcode.c @@ -0,0 +1,77 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Result Code | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 33 for Result Code +Length: 4 + +********************************************************************/ + +struct capwap_resultcode_raw_element { + unsigned long code; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_resultcode_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_resultcode_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_resultcode_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_resultcode_raw_element)); + element->type = htons(CAPWAP_ELEMENT_RESULTCODE); + element->length = htons(sizeof(struct capwap_resultcode_raw_element)); + + ((struct capwap_resultcode_raw_element*)element->data)->code = htonl(((struct capwap_resultcode_element*)data)->code); + + return element; +} + +/* */ +int capwap_resultcode_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_resultcode_element_parsing(struct capwap_message_element* element) { + struct capwap_resultcode_element* data; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_RESULTCODE); + + if (ntohs(element->length) != sizeof(struct capwap_resultcode_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_resultcode_element*)capwap_alloc(sizeof(struct capwap_resultcode_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->code = ntohl(((struct capwap_resultcode_raw_element*)element->data)->code); + return data; +} + +/* */ +void capwap_resultcode_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_resultcode.h b/src/common/capwap_element_resultcode.h new file mode 100644 index 0000000..13c022b --- /dev/null +++ b/src/common/capwap_element_resultcode.h @@ -0,0 +1,45 @@ +#ifndef __CAPWAP_ELEMENT_RESULTCODE_HEADER__ +#define __CAPWAP_ELEMENT_RESULTCODE_HEADER__ + +#define CAPWAP_ELEMENT_RESULTCODE 33 + +#define CAPWAP_RESULTCODE_SUCCESS 0 +#define CAPWAP_RESULTCODE_FAILURE 1 +#define CAPWAP_RESULTCODE_SUCCESS_NAT_DETECTED 2 +#define CAPWAP_RESULTCODE_JOIN_FAILURE_UNSPECIFIED 3 +#define CAPWAP_RESULTCODE_JOIN_FAILURE_RESOURCE_DEPLETION 4 +#define CAPWAP_RESULTCODE_JOIN_FAILURE_UNKNOWN_SOURCE 5 +#define CAPWAP_RESULTCODE_JOIN_FAILURE_INCORRECT_DATA 6 +#define CAPWAP_RESULTCODE_JOIN_FAILURE_ID_ALREADY_IN_USE 7 +#define CAPWAP_RESULTCODE_JOIN_FAILURE_WTP_HARDWARE_NOT_SUPPORTED 8 +#define CAPWAP_RESULTCODE_JOIN_FAILURE_BINDING_NOT_SUPPORTED 9 +#define CAPWAP_RESULTCODE_RESET_FAILURE_UNABLE_TO_RESET 10 +#define CAPWAP_RESULTCODE_RESET_FAILURE_FIRMWARE_WRITE_ERROR 11 +#define CAPWAP_RESULTCODE_CONF_FAILURE_SERVICE_PROVIDED_ANYHOW 12 +#define CAPWAP_RESULTCODE_CONF_FAILURE_SERVICE_NOT_PROVIDED 13 +#define CAPWAP_RESULTCODE_IMAGE_ERROR_INVALID_CHECKSUM 14 +#define CAPWAP_RESULTCODE_IMAGE_ERROR_INVALID_DATA_LENGTH 15 +#define CAPWAP_RESULTCODE_IMAGE_ERROR_OTHER_ERROR 16 +#define CAPWAP_RESULTCODE_IMAGE_ERROR_IMAGE_ALREADY_PRESENT 17 +#define CAPWAP_RESULTCODE_MSG_UNEXPECTED_INVALID_CURRENT_STATE 18 +#define CAPWAP_RESULTCODE_MSG_UNEXPECTED_UNRECOGNIZED_REQUEST 19 +#define CAPWAP_RESULTCODE_FAILURE_MISSING_MANDATORY_MSG_ELEMENT 20 +#define CAPWAP_RESULTCODE_FAILURE_UNRECOGNIZED_MESSAGE_ELEMENT 21 +#define CAPWAP_RESULTCODE_DATA_TRANSFER_ERROR 22 + +struct capwap_resultcode_element { + unsigned long code; +}; + +struct capwap_message_element* capwap_resultcode_element_create(void* data, unsigned long length); +int capwap_resultcode_element_validate(struct capwap_message_element* element); +void* capwap_resultcode_element_parsing(struct capwap_message_element* element); +void capwap_resultcode_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_RESULTCODE_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_RESULTCODE); \ + f->create(x, sizeof(struct capwap_resultcode_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_RESULTCODE_HEADER__ */ diff --git a/src/common/capwap_element_returnedmessage.c b/src/common/capwap_element_returnedmessage.c new file mode 100644 index 0000000..90053e3 --- /dev/null +++ b/src/common/capwap_element_returnedmessage.c @@ -0,0 +1,93 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Reason | Length | Message Element... ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 34 for Returned Message Element +Length: >= 6 + +********************************************************************/ + +struct capwap_returnedmessage_raw_element { + unsigned char reason; + unsigned char length; + char message[0]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_returnedmessage_element_create(void* data, unsigned long datalength) { + unsigned short length; + struct capwap_message_element* element; + struct capwap_returnedmessage_raw_element* dataraw; + struct capwap_returnedmessage_element* dataelement = (struct capwap_returnedmessage_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_returnedmessage_element)); + + /* Alloc block of memory */ + length = sizeof(struct capwap_returnedmessage_raw_element) + dataelement->length; + element = capwap_alloc(sizeof(struct capwap_message_element) + length); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element)); + element->type = htons(CAPWAP_ELEMENT_RETURNEDMESSAGE); + element->length = htons(length); + + dataraw = (struct capwap_returnedmessage_raw_element*)element->data; + dataraw->reason = dataelement->reason; + dataraw->length = dataelement->length; + memcpy(&dataraw->message[0], &dataelement->message[0], dataelement->length); + + return element; +} + +/* */ +int capwap_returnedmessage_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_returnedmessage_element_parsing(struct capwap_message_element* element) { + unsigned short length; + struct capwap_returnedmessage_element* data; + struct capwap_returnedmessage_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_RETURNEDMESSAGE); + + length = ntohs(element->length) - sizeof(struct capwap_returnedmessage_raw_element); + if (length > CAPWAP_RETURNED_MESSAGE_MAX_LENGTH) { + return NULL; + } + + /* */ + dataraw = (struct capwap_returnedmessage_raw_element*)element->data; + data = (struct capwap_returnedmessage_element*)capwap_alloc(sizeof(struct capwap_returnedmessage_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->reason = dataraw->reason; + data->length = dataraw->length; + memcpy(&data->message[0], &dataraw->message[0], dataraw->length); + + return data; +} + +/* */ +void capwap_returnedmessage_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_returnedmessage.h b/src/common/capwap_element_returnedmessage.h new file mode 100644 index 0000000..a127729 --- /dev/null +++ b/src/common/capwap_element_returnedmessage.h @@ -0,0 +1,30 @@ +#ifndef __CAPWAP_ELEMENT_RETURNEDMESSAGE_HEADER__ +#define __CAPWAP_ELEMENT_RETURNEDMESSAGE_HEADER__ + +#define CAPWAP_ELEMENT_RETURNEDMESSAGE 34 + +#define CAPWAP_REASON_UNKNOWN_MESSAGE_ELEMENT 1 +#define CAPWAP_REASON_UNSUPPORTED_MESSAGE_ELEMENT 2 +#define CAPWAP_REASON_UNKNOWN_MESSAGE_ELEMENT_VALUE 3 +#define CAPWAP_REASON_UNSUPPORTED_MESSAGE_ELEMENT_VALUE 4 + +#define CAPWAP_RETURNED_MESSAGE_MAX_LENGTH 255 + +struct capwap_returnedmessage_element { + unsigned char reason; + unsigned char length; + char message[CAPWAP_RETURNED_MESSAGE_MAX_LENGTH]; +}; + +struct capwap_message_element* capwap_returnedmessage_element_create(void* data, unsigned long length); +int capwap_returnedmessage_element_validate(struct capwap_message_element* element); +void* capwap_returnedmessage_element_parsing(struct capwap_message_element* element); +void capwap_returnedmessage_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_RETURNEDMESSAGE_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_RETURNEDMESSAGE); \ + f->create(x, sizeof(struct capwap_returnedmessage_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_RETURNEDMESSAGE_HEADER__ */ diff --git a/src/common/capwap_element_sessionid.c b/src/common/capwap_element_sessionid.c new file mode 100644 index 0000000..12e54ee --- /dev/null +++ b/src/common/capwap_element_sessionid.c @@ -0,0 +1,115 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Session ID | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Session ID | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Session ID | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Session ID | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +Type: 35 for Session ID +Length: 16 + +********************************************************************/ + +struct capwap_sessionid_raw_element { + unsigned char id[16]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_sessionid_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_sessionid_raw_element* dataraw; + struct capwap_sessionid_element* dataelement = (struct capwap_sessionid_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_sessionid_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_sessionid_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_sessionid_element)); + element->type = htons(CAPWAP_ELEMENT_SESSIONID); + element->length = htons(sizeof(struct capwap_sessionid_element)); + + dataraw = (struct capwap_sessionid_raw_element*)element->data; + memcpy(&dataraw->id[0], &dataelement->id[0], sizeof(unsigned char) * 16); + return element; +} + +/* */ +int capwap_sessionid_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_sessionid_element_parsing(struct capwap_message_element* element) { + struct capwap_sessionid_element* data; + struct capwap_sessionid_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_SESSIONID); + + if (ntohs(element->length) != sizeof(struct capwap_sessionid_raw_element)) { + return NULL; + } + + /* */ + dataraw = (struct capwap_sessionid_raw_element*)element->data; + data = (struct capwap_sessionid_element*)capwap_alloc(sizeof(struct capwap_sessionid_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + memcpy(&data->id[0], &dataraw->id[0], sizeof(unsigned char) * 16); + return data; +} + +/* */ +void capwap_sessionid_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} + +/* */ +void capwap_sessionid_generate(struct capwap_sessionid_element* session) { + int i; + + ASSERT(session != NULL); + + for (i = 0; i < 16; i++) { + session->id[i] = (unsigned char)capwap_get_rand(256); + } +} + +/* */ +void capwap_sessionid_printf(struct capwap_sessionid_element* session, char* string) { + int i; + char* pos = string; + + ASSERT(session != NULL); + ASSERT(string != NULL); + + for (i = 0; i < 16; i++) { + sprintf(pos, "%02x", session->id[i]); + pos += 2; + } + + *pos = 0; +} diff --git a/src/common/capwap_element_sessionid.h b/src/common/capwap_element_sessionid.h new file mode 100644 index 0000000..d982e57 --- /dev/null +++ b/src/common/capwap_element_sessionid.h @@ -0,0 +1,24 @@ +#ifndef __CAPWAP_ELEMENT_SESSIONID_HEADER__ +#define __CAPWAP_ELEMENT_SESSIONID_HEADER__ + +#define CAPWAP_ELEMENT_SESSIONID 35 + +struct capwap_sessionid_element { + unsigned char id[16]; +}; + +struct capwap_message_element* capwap_sessionid_element_create(void* data, unsigned long datalength); +int capwap_sessionid_element_validate(struct capwap_message_element* element); +void* capwap_sessionid_element_parsing(struct capwap_message_element* element); +void capwap_sessionid_element_free(void* data); + +void capwap_sessionid_generate(struct capwap_sessionid_element* session); +void capwap_sessionid_printf(struct capwap_sessionid_element* session, char* string); + +/* Helper */ +#define CAPWAP_CREATE_SESSIONID_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_SESSIONID); \ + f->create(x, sizeof(struct capwap_sessionid_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_SESSIONID_HEADER__ */ diff --git a/src/common/capwap_element_statisticstimer.c b/src/common/capwap_element_statisticstimer.c new file mode 100644 index 0000000..ee11a3b --- /dev/null +++ b/src/common/capwap_element_statisticstimer.c @@ -0,0 +1,77 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Statistics Timer | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 36 for Statistics Timer +Length: 2 + +********************************************************************/ + +struct capwap_statisticstimer_raw_element { + unsigned short timer; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_statisticstimer_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_statisticstimer_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_statisticstimer_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_statisticstimer_raw_element)); + element->type = htons(CAPWAP_ELEMENT_STATISTICSTIMER); + element->length = htons(sizeof(struct capwap_statisticstimer_raw_element)); + + ((struct capwap_statisticstimer_raw_element*)element->data)->timer = htons(((struct capwap_statisticstimer_element*)data)->timer); + + return element; +} + +/* */ +int capwap_statisticstimer_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_statisticstimer_element_parsing(struct capwap_message_element* element) { + struct capwap_statisticstimer_element* data; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_STATISTICSTIMER); + + if (ntohs(element->length) != sizeof(struct capwap_statisticstimer_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_statisticstimer_element*)capwap_alloc(sizeof(struct capwap_statisticstimer_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->timer = ntohs(((struct capwap_statisticstimer_raw_element*)element->data)->timer); + return data; +} + +/* */ +void capwap_statisticstimer_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_statisticstimer.h b/src/common/capwap_element_statisticstimer.h new file mode 100644 index 0000000..c4ceac6 --- /dev/null +++ b/src/common/capwap_element_statisticstimer.h @@ -0,0 +1,21 @@ +#ifndef __CAPWAP_ELEMENT_STATISTICSTIMER_HEADER__ +#define __CAPWAP_ELEMENT_STATISTICSTIMER_HEADER__ + +#define CAPWAP_ELEMENT_STATISTICSTIMER 36 + +struct capwap_statisticstimer_element { + unsigned short timer; +}; + +struct capwap_message_element* capwap_statisticstimer_element_create(void* data, unsigned long length); +int capwap_statisticstimer_element_validate(struct capwap_message_element* element); +void* capwap_statisticstimer_element_parsing(struct capwap_message_element* element); +void capwap_statisticstimer_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_STATISTICSTIMER_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_STATISTICSTIMER); \ + f->create(x, sizeof(struct capwap_statisticstimer_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_STATISTICSTIMER_HEADER__ */ diff --git a/src/common/capwap_element_timers.c b/src/common/capwap_element_timers.c new file mode 100644 index 0000000..46ca9bb --- /dev/null +++ b/src/common/capwap_element_timers.c @@ -0,0 +1,86 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Discovery | Echo Request | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 12 for CAPWAP Timers +Length: 2 + +********************************************************************/ + +struct capwap_timers_raw_element { + unsigned char discovery; + unsigned char echorequest; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_timers_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_timers_element* dataelement = (struct capwap_timers_element*)data; + struct capwap_timers_raw_element* dataraw; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_timers_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_timers_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_timers_raw_element)); + element->type = htons(CAPWAP_ELEMENT_TIMERS); + element->length = htons(sizeof(struct capwap_timers_raw_element)); + + dataraw = (struct capwap_timers_raw_element*)element->data; + dataraw->discovery = dataelement->discovery; + dataraw->echorequest = dataelement->echorequest; + + return element; +} + +/* */ +int capwap_timers_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_timers_element_parsing(struct capwap_message_element* element) { + struct capwap_timers_element* data; + struct capwap_timers_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_TIMERS); + + if (ntohs(element->length) != sizeof(struct capwap_timers_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_timers_element*)capwap_alloc(sizeof(struct capwap_timers_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + dataraw = (struct capwap_timers_raw_element*)element->data; + data->discovery = dataraw->discovery; + data->echorequest = dataraw->echorequest; + + return data; +} + +/* */ +void capwap_timers_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_timers.h b/src/common/capwap_element_timers.h new file mode 100644 index 0000000..22700e7 --- /dev/null +++ b/src/common/capwap_element_timers.h @@ -0,0 +1,22 @@ +#ifndef __CAPWAP_ELEMENT_TIMERS_HEADER__ +#define __CAPWAP_ELEMENT_TIMERS_HEADER__ + +#define CAPWAP_ELEMENT_TIMERS 12 + +struct capwap_timers_element { + unsigned char discovery; + unsigned char echorequest; +}; + +struct capwap_message_element* capwap_timers_element_create(void* data, unsigned long length); +int capwap_timers_element_validate(struct capwap_message_element* element); +void* capwap_timers_element_parsing(struct capwap_message_element* element); +void capwap_timers_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_TIMERS_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_TIMERS); \ + f->create(x, sizeof(struct capwap_timers_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_TIMERS_HEADER__ */ diff --git a/src/common/capwap_element_transport.c b/src/common/capwap_element_transport.c new file mode 100644 index 0000000..d03eb21 --- /dev/null +++ b/src/common/capwap_element_transport.c @@ -0,0 +1,77 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 + 0 1 2 3 4 5 6 7 ++-+-+-+-+-+-+-+-+ +| Transport | ++-+-+-+-+-+-+-+-+ + +Type: 51 for CAPWAP Transport Protocol +Length: 1 + +********************************************************************/ + +struct capwap_transport_raw_element { + char type; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_transport_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_transport_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_transport_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_transport_raw_element)); + element->type = htons(CAPWAP_ELEMENT_TRANSPORT); + element->length = htons(sizeof(struct capwap_transport_raw_element)); + + ((struct capwap_transport_raw_element*)element->data)->type = ((struct capwap_transport_element*)data)->type; + + return element; +} + +/* */ +int capwap_transport_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_transport_element_parsing(struct capwap_message_element* element) { + struct capwap_transport_element* data; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_TRANSPORT); + + if (ntohs(element->length) != sizeof(struct capwap_transport_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_transport_element*)capwap_alloc(sizeof(struct capwap_transport_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->type = ((struct capwap_transport_raw_element*)element->data)->type; + return data; +} + +/* */ +void capwap_transport_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_transport.h b/src/common/capwap_element_transport.h new file mode 100644 index 0000000..924f7b5 --- /dev/null +++ b/src/common/capwap_element_transport.h @@ -0,0 +1,24 @@ +#ifndef __CAPWAP_ELEMENT_TRANSPORT_HEADER__ +#define __CAPWAP_ELEMENT_TRANSPORT_HEADER__ + +#define CAPWAP_ELEMENT_TRANSPORT 51 + +struct capwap_transport_element { + char type; +}; + +#define CAPWAP_UDPLITE_TRANSPORT 1 +#define CAPWAP_UDP_TRANSPORT 2 + +struct capwap_message_element* capwap_transport_element_create(void* data, unsigned long length); +int capwap_transport_element_validate(struct capwap_message_element* element); +void* capwap_transport_element_parsing(struct capwap_message_element* element); +void capwap_transport_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_TRANSPORT_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_TRANSPORT); \ + f->create(x, sizeof(struct capwap_transport_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_TRANSPORT_HEADER__ */ diff --git a/src/common/capwap_element_vendorpayload.c b/src/common/capwap_element_vendorpayload.c new file mode 100644 index 0000000..c875817 --- /dev/null +++ b/src/common/capwap_element_vendorpayload.c @@ -0,0 +1,99 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Vendor Identifier | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Element ID | Data... ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 37 for Vendor Specific Payload +Length: >= 7 + +********************************************************************/ + +struct capwap_vendorpayload_raw_element { + unsigned long vendorid; + unsigned short elementid; + char data[0]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_vendorpayload_element_create(void* data, unsigned long datalength) { + unsigned short elementlength; + struct capwap_message_element* element; + struct capwap_vendorpayload_raw_element* dataraw; + struct capwap_vendorpayload_element* dataelement = (struct capwap_vendorpayload_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_vendorpayload_element)); + + /* */ + if (!dataelement->datalength || (dataelement->datalength > CAPWAP_VENDORPAYLOAD_MAXLENGTH)) { + return NULL; + } + + /* Alloc block of memory */ + elementlength = sizeof(struct capwap_vendorpayload_raw_element) + dataelement->datalength; + element = capwap_alloc(sizeof(struct capwap_message_element) + elementlength); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + elementlength); + element->type = htons(CAPWAP_ELEMENT_VENDORPAYLOAD); + element->length = htons(elementlength); + + dataraw = (struct capwap_vendorpayload_raw_element*)element->data; + dataraw->vendorid = htonl(dataelement->vendorid); + dataraw->elementid = htons(dataelement->elementid); + memcpy(&dataraw->data[0], &dataelement->data[0], dataelement->datalength); + return element; +} + +/* */ +int capwap_vendorpayload_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_vendorpayload_element_parsing(struct capwap_message_element* element) { + unsigned short elementlength; + struct capwap_vendorpayload_element* data; + struct capwap_vendorpayload_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_VENDORPAYLOAD); + + elementlength = ntohs(element->length); + if (elementlength > sizeof(struct capwap_vendorpayload_raw_element)) { + return NULL; + } + + /* */ + dataraw = (struct capwap_vendorpayload_raw_element*)element->data; + data = (struct capwap_vendorpayload_element*)capwap_alloc(sizeof(struct capwap_vendorpayload_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->vendorid = ntohl(dataraw->vendorid); + data->elementid = ntohs(dataraw->elementid); + data->datalength = elementlength - sizeof(struct capwap_vendorpayload_element); + memcpy(&data->data[0], &dataraw->data[0], data->datalength); + return data; +} + +/* */ +void capwap_vendorpayload_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_vendorpayload.h b/src/common/capwap_element_vendorpayload.h new file mode 100644 index 0000000..e664aa0 --- /dev/null +++ b/src/common/capwap_element_vendorpayload.h @@ -0,0 +1,27 @@ +#ifndef __CAPWAP_ELEMENT_VENDORPAYLOAD_HEADER__ +#define __CAPWAP_ELEMENT_VENDORPAYLOAD_HEADER__ + +#define CAPWAP_ELEMENT_VENDORPAYLOAD 37 + +#define CAPWAP_VENDORPAYLOAD_MAXLENGTH 2048 + +struct capwap_vendorpayload_element { + unsigned long vendorid; + unsigned short elementid; + unsigned short datalength; + char data[CAPWAP_VENDORPAYLOAD_MAXLENGTH]; +}; + +struct capwap_message_element* capwap_vendorpayload_element_create(void* data, unsigned long datalength); +int capwap_vendorpayload_element_validate(struct capwap_message_element* element); +void* capwap_vendorpayload_element_parsing(struct capwap_message_element* element); +void capwap_vendorpayload_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_VENDORPAYLOAD_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_VENDORPAYLOAD_MAXLENGTH); \ + f->create(x, sizeof(struct capwap_vendorpayload_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_VENDORPAYLOAD_HEADER__ */ diff --git a/src/common/capwap_element_wtpboarddata.c b/src/common/capwap_element_wtpboarddata.c new file mode 100644 index 0000000..63f7a56 --- /dev/null +++ b/src/common/capwap_element_wtpboarddata.c @@ -0,0 +1,161 @@ +#include "capwap.h" +#include "capwap_array.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Vendor Identifier | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Board Data Sub-Element... ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Board Data Type | Board Data Length | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Board Data Value... ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 38 for WTP Board Data +Length: >=14 + +********************************************************************/ + +struct capwap_wtpboarddata_raw_element { + unsigned long vendor; + char data[0]; +} __attribute__((__packed__)); + +struct capwap_wtpboarddata_raw_board_subelement { + unsigned short type; + unsigned short length; + char data[0]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_wtpboarddata_element_create(void* data, unsigned long datalength) { + char* pos; + unsigned long i; + unsigned short length; + struct capwap_message_element* element; + struct capwap_wtpboarddata_raw_element* dataraw; + struct capwap_wtpboarddata_element* dataelement = (struct capwap_wtpboarddata_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength >= sizeof(struct capwap_wtpboarddata_element)); + ASSERT(dataelement->boardsubelement != NULL); + + /* Calc length packet */ + length = sizeof(struct capwap_wtpboarddata_raw_element); + for (i = 0; i < dataelement->boardsubelement->count; i++) { + struct capwap_wtpboarddata_board_subelement* board = (struct capwap_wtpboarddata_board_subelement*)capwap_array_get_item_pointer(dataelement->boardsubelement, i); + length += sizeof(struct capwap_wtpboarddata_raw_board_subelement) + board->length; + } + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + length); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + length); + element->type = htons(CAPWAP_ELEMENT_WTPBOARDDATA); + element->length = htons(length); + + /* */ + dataraw = (struct capwap_wtpboarddata_raw_element*)element->data; + dataraw->vendor = htonl(dataelement->vendor); + pos = dataraw->data; + + /* Board Sub-Element */ + for (i = 0; i < dataelement->boardsubelement->count; i++) { + struct capwap_wtpboarddata_raw_board_subelement* boardraw = (struct capwap_wtpboarddata_raw_board_subelement*)pos; + struct capwap_wtpboarddata_board_subelement* board = (struct capwap_wtpboarddata_board_subelement*)capwap_array_get_item_pointer(dataelement->boardsubelement, i); + + boardraw->type = htons(board->type); + boardraw->length = htons(board->length); + memcpy(boardraw->data, board->data, board->length); + + pos += sizeof(struct capwap_wtpboarddata_raw_board_subelement) + board->length; + } + + return element; +} + +/* */ +int capwap_wtpboarddata_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_wtpboarddata_element_parsing(struct capwap_message_element* element) { + long i; + char* pos; + long length; + struct capwap_wtpboarddata_element* data; + struct capwap_wtpboarddata_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_WTPBOARDDATA); + + length = (long)ntohs(element->length); + if (length < 14) { + capwap_logging_debug("Invalid WTP Board Data element"); + return NULL; + } + + /* */ + dataraw = (struct capwap_wtpboarddata_raw_element*)element->data; + data = (struct capwap_wtpboarddata_element*)capwap_alloc(sizeof(struct capwap_wtpboarddata_element)); + data->boardsubelement = capwap_array_create(sizeof(struct capwap_wtpboarddata_board_subelement), 0); + + /* */ + data->vendor = ntohl(dataraw->vendor); + + pos = dataraw->data; + length -= sizeof(struct capwap_wtpboarddata_raw_element); + + /* Board Subelement */ + i = 0; + while (length > 0) { + struct capwap_wtpboarddata_board_subelement* board = (struct capwap_wtpboarddata_board_subelement*)capwap_array_get_item_pointer(data->boardsubelement, i); + struct capwap_wtpboarddata_raw_board_subelement* boardraw = (struct capwap_wtpboarddata_raw_board_subelement*)pos; + unsigned short boardlength = ntohs(boardraw->length); + unsigned short boardrawlength = sizeof(struct capwap_wtpboarddata_raw_board_subelement) + boardlength; + + if ((boardlength > CAPWAP_BOARD_SUBELEMENT_MAXDATA) || (length < boardrawlength)) { + capwap_logging_debug("Invalid WTP Board Data element"); + capwap_wtpboarddata_element_free(data); + return NULL; + } + + /* */ + board->type = ntohs(boardraw->type); + board->length = boardlength; + memcpy(board->data, boardraw->data, boardlength); + + /* */ + i++; + pos += boardrawlength; + length -= boardrawlength; + } + + return data; +} + +/* */ +void capwap_wtpboarddata_element_free(void* data) { + struct capwap_wtpboarddata_element* dataelement = (struct capwap_wtpboarddata_element*)data; + + ASSERT(dataelement != NULL); + ASSERT(dataelement->boardsubelement != NULL); + + capwap_array_free(dataelement->boardsubelement); + capwap_free(dataelement); +} diff --git a/src/common/capwap_element_wtpboarddata.h b/src/common/capwap_element_wtpboarddata.h new file mode 100644 index 0000000..250c9a2 --- /dev/null +++ b/src/common/capwap_element_wtpboarddata.h @@ -0,0 +1,35 @@ +#ifndef __CAPWAP_ELEMENT_WTPBOARDDATA_HEADER__ +#define __CAPWAP_ELEMENT_WTPBOARDDATA_HEADER__ + +#define CAPWAP_ELEMENT_WTPBOARDDATA 38 + +struct capwap_wtpboarddata_element { + unsigned long vendor; + struct capwap_array* boardsubelement; +}; + +#define CAPWAP_BOARD_SUBELEMENT_MODELNUMBER 0 +#define CAPWAP_BOARD_SUBELEMENT_SERIALNUMBER 1 +#define CAPWAP_BOARD_SUBELEMENT_ID 2 +#define CAPWAP_BOARD_SUBELEMENT_REVISION 3 +#define CAPWAP_BOARD_SUBELEMENT_MACADDRESS 4 +#define CAPWAP_BOARD_SUBELEMENT_MAXDATA 1024 + +struct capwap_wtpboarddata_board_subelement { + unsigned short type; + unsigned short length; + char data[CAPWAP_BOARD_SUBELEMENT_MAXDATA]; +}; + +struct capwap_message_element* capwap_wtpboarddata_element_create(void* data, unsigned long datalength); +int capwap_wtpboarddata_element_validate(struct capwap_message_element* element); +void* capwap_wtpboarddata_element_parsing(struct capwap_message_element* element); +void capwap_wtpboarddata_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_WTPBOARDDATA_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_WTPBOARDDATA); \ + f->create(x, sizeof(struct capwap_wtpboarddata_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_WTPBOARDDATA_HEADER__ */ diff --git a/src/common/capwap_element_wtpdescriptor.c b/src/common/capwap_element_wtpdescriptor.c new file mode 100644 index 0000000..4d3a393 --- /dev/null +++ b/src/common/capwap_element_wtpdescriptor.c @@ -0,0 +1,241 @@ +#include "capwap.h" +#include "capwap_array.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Max Radios | Radios in use | Num Encrypt |Encryp Sub-Elmt| ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Encryption Sub-Element | Descriptor Sub-Element... ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + 0 1 2 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +|Resvd| WBID | Encryption Capabilities | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Descriptor Vendor Identifier | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Descriptor Type | Descriptor Length | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Descriptor Data... ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 39 for WTP Descriptor +Length: >= 33 + +********************************************************************/ + +struct capwap_wtpdescriptor_raw_element { + unsigned char maxradios; + unsigned char radiosinuse; + unsigned char encryptcount; + char data[0]; +} __attribute__((__packed__)); + +struct capwap_wtpdescriptor_raw_encrypt_subelement { +#ifdef CAPWAP_BIG_ENDIAN + unsigned char reserved : 3; + unsigned char wbid : 5; +#else + unsigned char wbid : 5; + unsigned char reserved : 3; +#endif + unsigned short capabilities; +} __attribute__((__packed__)); + +struct capwap_wtpdescriptor_raw_desc_subelement { + unsigned long vendor; + unsigned short type; + unsigned short length; + char data[0]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_wtpdescriptor_element_create(void* data, unsigned long datalength) { + char* pos; + unsigned long i; + unsigned short length; + struct capwap_message_element* element; + struct capwap_wtpdescriptor_raw_element* dataraw; + struct capwap_wtpdescriptor_element* dataelement = (struct capwap_wtpdescriptor_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength >= sizeof(struct capwap_wtpdescriptor_element)); + ASSERT(dataelement->encryptsubelement != NULL); + ASSERT(dataelement->descsubelement != NULL); + + /* Calc length packet */ + length = sizeof(struct capwap_wtpdescriptor_raw_element); + length += dataelement->encryptsubelement->count * sizeof(struct capwap_wtpdescriptor_raw_encrypt_subelement); + for (i = 0; i < dataelement->descsubelement->count; i++) { + struct capwap_wtpdescriptor_desc_subelement* desc = (struct capwap_wtpdescriptor_desc_subelement*)capwap_array_get_item_pointer(dataelement->descsubelement, i); + length += sizeof(struct capwap_wtpdescriptor_raw_desc_subelement) + desc->length; + } + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + length); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + length); + element->type = htons(CAPWAP_ELEMENT_WTPDESCRIPTOR); + element->length = htons(length); + + /* Descriptor */ + dataraw = (struct capwap_wtpdescriptor_raw_element*)element->data; + dataraw->maxradios = dataelement->maxradios; + dataraw->radiosinuse = dataelement->radiosinuse; + dataraw->encryptcount = (unsigned char)dataelement->encryptsubelement->count; + pos = dataraw->data; + + /* Encryption Sub-Element */ + for (i = 0; i < dataelement->encryptsubelement->count; i++) { + struct capwap_wtpdescriptor_raw_encrypt_subelement* encryptraw = (struct capwap_wtpdescriptor_raw_encrypt_subelement*)pos; + struct capwap_wtpdescriptor_encrypt_subelement* encrypt = (struct capwap_wtpdescriptor_encrypt_subelement*)capwap_array_get_item_pointer(dataelement->encryptsubelement, i); + + encryptraw->wbid = encrypt->wbid; + encryptraw->capabilities = htons(encrypt->capabilities); + + pos += sizeof(struct capwap_wtpdescriptor_raw_encrypt_subelement); + } + + /* Descriptor Sub-Element */ + for (i = 0; i < dataelement->descsubelement->count; i++) { + struct capwap_wtpdescriptor_raw_desc_subelement* descraw = (struct capwap_wtpdescriptor_raw_desc_subelement*)pos; + struct capwap_wtpdescriptor_desc_subelement* desc = (struct capwap_wtpdescriptor_desc_subelement*)capwap_array_get_item_pointer(dataelement->descsubelement, i); + + descraw->vendor = htonl(desc->vendor); + descraw->type = htons(desc->type); + descraw->length = htons(desc->length); + memcpy(descraw->data, desc->data, desc->length); + + pos += sizeof(struct capwap_wtpdescriptor_raw_desc_subelement) + desc->length; + } + + return element; +} + +/* */ +int capwap_wtpdescriptor_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_wtpdescriptor_element_parsing(struct capwap_message_element* element) { + unsigned char i; + long length; + char* pos; + struct capwap_wtpdescriptor_element* data; + struct capwap_wtpdescriptor_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_WTPDESCRIPTOR); + + length = (long)ntohs(element->length); + if (length < 33) { + capwap_logging_debug("Invalid WTP Descriptor element"); + return NULL; + } + + /* */ + dataraw = (struct capwap_wtpdescriptor_raw_element*)element->data; + if ((dataraw->radiosinuse > dataraw->maxradios) || (dataraw->encryptcount == 0)) { + capwap_logging_debug("Invalid WTP Descriptor element"); + return NULL; + } + + /* */ + data = (struct capwap_wtpdescriptor_element*)capwap_alloc(sizeof(struct capwap_wtpdescriptor_element)); + if (!data) { + capwap_outofmemory(); + } + + data->encryptsubelement = capwap_array_create(sizeof(struct capwap_wtpdescriptor_encrypt_subelement), 0); + data->descsubelement = capwap_array_create(sizeof(struct capwap_wtpdescriptor_desc_subelement), 0); + + /* */ + data->maxradios = dataraw->maxradios; + data->radiosinuse = dataraw->radiosinuse; + capwap_array_resize(data->encryptsubelement, dataraw->encryptcount); + + pos = dataraw->data; + length -= sizeof(struct capwap_wtpdescriptor_raw_element); + + /* Encrypt Subelement */ + for (i = 0; i < dataraw->encryptcount; i++) { + struct capwap_wtpdescriptor_encrypt_subelement* encrypt = (struct capwap_wtpdescriptor_encrypt_subelement*)capwap_array_get_item_pointer(data->encryptsubelement, i); + struct capwap_wtpdescriptor_raw_encrypt_subelement* encryptraw = (struct capwap_wtpdescriptor_raw_encrypt_subelement*)pos; + + if (length < sizeof(struct capwap_wtpdescriptor_raw_element)) { + capwap_logging_debug("Invalid WTP Descriptor element"); + capwap_wtpdescriptor_element_free(data); + return NULL; + } + + /* */ + encrypt->wbid = encryptraw->wbid; + encrypt->capabilities = ntohs(encryptraw->capabilities); + + /* */ + pos += sizeof(struct capwap_wtpdescriptor_raw_encrypt_subelement); + length -= sizeof(struct capwap_wtpdescriptor_raw_element); + } + + if (length < 0) { + capwap_logging_debug("Invalid WTP Descriptor element"); + capwap_wtpdescriptor_element_free(data); + return NULL; + } + + /* Description Subelement */ + i = 0; + while (length > 0) { + struct capwap_wtpdescriptor_desc_subelement* desc = (struct capwap_wtpdescriptor_desc_subelement*)capwap_array_get_item_pointer(data->descsubelement, i); + struct capwap_wtpdescriptor_raw_desc_subelement* descraw = (struct capwap_wtpdescriptor_raw_desc_subelement*)pos; + unsigned short desclength = ntohs(descraw->length); + unsigned short descrawlength = sizeof(struct capwap_wtpdescriptor_raw_desc_subelement) + desclength; + + if ((desclength > CAPWAP_WTPDESC_SUBELEMENT_MAXDATA) || (length < descrawlength)) { + capwap_logging_debug("Invalid WTP Descriptor element"); + capwap_wtpdescriptor_element_free(data); + return NULL; + } + + /* */ + desc->vendor = ntohl(descraw->vendor); + desc->type = ntohs(descraw->type); + desc->length = desclength; + memcpy(desc->data, descraw->data, desclength); + + /* */ + i++; + pos += descrawlength; + length -= descrawlength; + } + + return data; +} + +/* */ +void capwap_wtpdescriptor_element_free(void* data) { + struct capwap_wtpdescriptor_element* dataelement = (struct capwap_wtpdescriptor_element*)data; + + ASSERT(dataelement != NULL); + ASSERT(dataelement->encryptsubelement != NULL); + ASSERT(dataelement->descsubelement != NULL); + + capwap_array_free(dataelement->encryptsubelement); + capwap_array_free(dataelement->descsubelement); + capwap_free(dataelement); +} diff --git a/src/common/capwap_element_wtpdescriptor.h b/src/common/capwap_element_wtpdescriptor.h new file mode 100644 index 0000000..a9ebf86 --- /dev/null +++ b/src/common/capwap_element_wtpdescriptor.h @@ -0,0 +1,42 @@ +#ifndef __CAPWAP_ELEMENT_WTPDESCRIPTOR_HEADER__ +#define __CAPWAP_ELEMENT_WTPDESCRIPTOR_HEADER__ + +#define CAPWAP_ELEMENT_WTPDESCRIPTOR 39 + +struct capwap_wtpdescriptor_element { + unsigned char maxradios; + unsigned char radiosinuse; + struct capwap_array* encryptsubelement; + struct capwap_array* descsubelement; +}; + +struct capwap_wtpdescriptor_encrypt_subelement { + unsigned char wbid; + unsigned short capabilities; +}; + +#define CAPWAP_WTPDESC_SUBELEMENT_HARDWAREVERSION 0 +#define CAPWAP_WTPDESC_SUBELEMENT_SOFTWAREVERSION 1 +#define CAPWAP_WTPDESC_SUBELEMENT_BOOTVERSION 2 +#define CAPWAP_WTPDESC_SUBELEMENT_OTHERVERSION 3 +#define CAPWAP_WTPDESC_SUBELEMENT_MAXDATA 1024 + +struct capwap_wtpdescriptor_desc_subelement { + unsigned long vendor; + unsigned short type; + unsigned short length; + char data[CAPWAP_WTPDESC_SUBELEMENT_MAXDATA]; +}; + +struct capwap_message_element* capwap_wtpdescriptor_element_create(void* data, unsigned long datalength); +int capwap_wtpdescriptor_element_validate(struct capwap_message_element* element); +void* capwap_wtpdescriptor_element_parsing(struct capwap_message_element* element); +void capwap_wtpdescriptor_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_WTPDESCRIPTOR_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_WTPDESCRIPTOR); \ + f->create(x, sizeof(struct capwap_wtpdescriptor_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_WTPDESCRIPTOR_HEADER__ */ diff --git a/src/common/capwap_element_wtpfallback.c b/src/common/capwap_element_wtpfallback.c new file mode 100644 index 0000000..d77d9e9 --- /dev/null +++ b/src/common/capwap_element_wtpfallback.c @@ -0,0 +1,77 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 + 0 1 2 3 4 5 6 7 ++-+-+-+-+-+-+-+-+ +| Mode | ++-+-+-+-+-+-+-+-+ + +Type: 40 for WTP Fallback +Length: 1 + +********************************************************************/ + +struct capwap_wtpfallback_raw_element { + char mode; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_wtpfallback_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_wtpfallback_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_wtpfallback_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_wtpfallback_raw_element)); + element->type = htons(CAPWAP_ELEMENT_WTPFALLBACK); + element->length = htons(sizeof(struct capwap_wtpfallback_raw_element)); + + ((struct capwap_wtpfallback_raw_element*)element->data)->mode = ((struct capwap_wtpfallback_element*)data)->mode; + + return element; +} + +/* */ +int capwap_wtpfallback_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_wtpfallback_element_parsing(struct capwap_message_element* element) { + struct capwap_wtpfallback_element* data; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_WTPFALLBACK); + + if (ntohs(element->length) != sizeof(struct capwap_wtpfallback_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_wtpfallback_element*)capwap_alloc(sizeof(struct capwap_wtpfallback_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->mode = ((struct capwap_wtpfallback_raw_element*)element->data)->mode; + return data; +} + +/* */ +void capwap_wtpfallback_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_wtpfallback.h b/src/common/capwap_element_wtpfallback.h new file mode 100644 index 0000000..2beebf2 --- /dev/null +++ b/src/common/capwap_element_wtpfallback.h @@ -0,0 +1,24 @@ +#ifndef __CAPWAP_ELEMENT_WTPFALLBACK_HEADER__ +#define __CAPWAP_ELEMENT_WTPFALLBACK_HEADER__ + +#define CAPWAP_ELEMENT_WTPFALLBACK 40 + +struct capwap_wtpfallback_element { + char mode; +}; + +#define CAPWAP_WTP_FALLBACK_ENABLED 1 +#define CAPWAP_WTP_FALLBACK_DISABLED 2 + +struct capwap_message_element* capwap_wtpfallback_element_create(void* data, unsigned long length); +int capwap_wtpfallback_element_validate(struct capwap_message_element* element); +void* capwap_wtpfallback_element_parsing(struct capwap_message_element* element); +void capwap_wtpfallback_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_WTPFALLBACK_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_WTPFALLBACK); \ + f->create(x, sizeof(struct capwap_ecnsupport_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_WTPFALLBACK_HEADER__ */ diff --git a/src/common/capwap_element_wtpframetunnelmode.c b/src/common/capwap_element_wtpframetunnelmode.c new file mode 100644 index 0000000..e70613f --- /dev/null +++ b/src/common/capwap_element_wtpframetunnelmode.c @@ -0,0 +1,81 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 + 0 1 2 3 4 5 6 7 ++-+-+-+-+-+-+-+-+ +|Reservd|N|E|L|U| ++-+-+-+-+-+-+-+-+ + +Type: 41 for WTP Frame Tunnel Mode +Length: 1 + +********************************************************************/ + +struct capwap_wtpframetunnelmode_raw_element { + unsigned char mode; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_wtpframetunnelmode_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_wtpframetunnelmode_raw_element* dataraw; + struct capwap_wtpframetunnelmode_element* dataelement = (struct capwap_wtpframetunnelmode_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength >= sizeof(struct capwap_wtpframetunnelmode_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_wtpframetunnelmode_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_wtpframetunnelmode_raw_element)); + element->type = htons(CAPWAP_ELEMENT_WTPFRAMETUNNELMODE); + element->length = htons(sizeof(struct capwap_wtpframetunnelmode_raw_element)); + + dataraw = (struct capwap_wtpframetunnelmode_raw_element*)element->data; + dataraw->mode = dataelement->mode & CAPWAP_WTP_FRAME_TUNNEL_MODE_MASK; + return element; +} + +/* */ +int capwap_wtpframetunnelmode_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_wtpframetunnelmode_element_parsing(struct capwap_message_element* element) { + struct capwap_wtpframetunnelmode_element* data; + struct capwap_wtpframetunnelmode_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_WTPFRAMETUNNELMODE); + + if (ntohs(element->length) != 1) { + return NULL; + } + + /* */ + dataraw = (struct capwap_wtpframetunnelmode_raw_element*)element->data; + data = (struct capwap_wtpframetunnelmode_element*)capwap_alloc(sizeof(struct capwap_wtpframetunnelmode_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->mode = dataraw->mode & CAPWAP_WTP_FRAME_TUNNEL_MODE_MASK; + return data; +} + +/* */ +void capwap_wtpframetunnelmode_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_wtpframetunnelmode.h b/src/common/capwap_element_wtpframetunnelmode.h new file mode 100644 index 0000000..c0cf0d0 --- /dev/null +++ b/src/common/capwap_element_wtpframetunnelmode.h @@ -0,0 +1,26 @@ +#ifndef __CAPWAP_ELEMENT_WTPFRAMETUNNELMODE_HEADER__ +#define __CAPWAP_ELEMENT_WTPFRAMETUNNELMODE_HEADER__ + +#define CAPWAP_ELEMENT_WTPFRAMETUNNELMODE 41 + +struct capwap_wtpframetunnelmode_element { + unsigned char mode; +}; + +#define CAPWAP_WTP_FRAME_TUNNEL_MODE_MASK 0x0e +#define CAPWAP_WTP_NATIVE_FRAME_TUNNEL 0x08 +#define CAPWAP_WTP_8023_FRAME_TUNNEL 0x04 +#define CAPWAP_WTP_LOCAL_BRIDGING 0x02 + +struct capwap_message_element* capwap_wtpframetunnelmode_element_create(void* data, unsigned long datalength); +int capwap_wtpframetunnelmode_element_validate(struct capwap_message_element* element); +void* capwap_wtpframetunnelmode_element_parsing(struct capwap_message_element* element); +void capwap_wtpframetunnelmode_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_WTPFRAMETUNNELMODE_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_WTPFRAMETUNNELMODE); \ + f->create(x, sizeof(struct capwap_wtpframetunnelmode_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_WTPFRAMETUNNELMODE_HEADER__ */ diff --git a/src/common/capwap_element_wtpmactype.c b/src/common/capwap_element_wtpmactype.c new file mode 100644 index 0000000..7b1a99f --- /dev/null +++ b/src/common/capwap_element_wtpmactype.c @@ -0,0 +1,77 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 + 0 1 2 3 4 5 6 7 ++-+-+-+-+-+-+-+-+ +| MAC Type | ++-+-+-+-+-+-+-+-+ + +Type: 44 for WTP MAC Type +Length: 1 + +********************************************************************/ + +struct capwap_wtpmactype_raw_element { + char type; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_wtpmactype_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_wtpmactype_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_wtpmactype_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_wtpmactype_raw_element)); + element->type = htons(CAPWAP_ELEMENT_WTPMACTYPE); + element->length = htons(sizeof(struct capwap_wtpmactype_raw_element)); + + ((struct capwap_wtpmactype_raw_element*)element->data)->type = ((struct capwap_wtpmactype_element*)data)->type; + + return element; +} + +/* */ +int capwap_wtpmactype_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_wtpmactype_element_parsing(struct capwap_message_element* element) { + struct capwap_wtpmactype_element* data; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_WTPMACTYPE); + + if (ntohs(element->length) != sizeof(struct capwap_wtpmactype_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_wtpmactype_element*)capwap_alloc(sizeof(struct capwap_wtpmactype_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->type = ((struct capwap_wtpmactype_raw_element*)element->data)->type; + return data; +} + +/* */ +void capwap_wtpmactype_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_wtpmactype.h b/src/common/capwap_element_wtpmactype.h new file mode 100644 index 0000000..870aa50 --- /dev/null +++ b/src/common/capwap_element_wtpmactype.h @@ -0,0 +1,24 @@ +#ifndef __CAPWAP_ELEMENT_WTPMACTYPE_HEADER__ +#define __CAPWAP_ELEMENT_WTPMACTYPE_HEADER__ + +#define CAPWAP_ELEMENT_WTPMACTYPE 44 + +struct capwap_wtpmactype_element { + char type; +}; + +#define CAPWAP_LOCALMAC 0 +#define CAPWAP_SPLITMAC 1 + +struct capwap_message_element* capwap_wtpmactype_element_create(void* data, unsigned long length); +int capwap_wtpmactype_element_validate(struct capwap_message_element* element); +void* capwap_wtpmactype_element_parsing(struct capwap_message_element* element); +void capwap_wtpmactype_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_WTPMACTYPE_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_WTPMACTYPE); \ + f->create(x, sizeof(struct capwap_wtpmactype_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_WTPMACTYPE_HEADER__ */ diff --git a/src/common/capwap_element_wtpname.c b/src/common/capwap_element_wtpname.c new file mode 100644 index 0000000..acc4382 --- /dev/null +++ b/src/common/capwap_element_wtpname.c @@ -0,0 +1,86 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 + 0 1 2 3 4 5 6 7 ++-+-+-+-+-+-+-+-+ +| Name ... ++-+-+-+-+-+-+-+-+ + +Type: 45 for WTP Name +Length: >= 1 + +********************************************************************/ + +struct capwap_wtpname_raw_element { + char name[0]; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_wtpname_element_create(void* data, unsigned long datalength) { + unsigned short namelength; + struct capwap_message_element* element; + struct capwap_wtpname_raw_element* dataraw; + struct capwap_wtpname_element* dataelement = (struct capwap_wtpname_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength >= sizeof(struct capwap_wtpname_element)); + + /* Alloc block of memory */ + namelength = strlen(dataelement->name); + element = capwap_alloc(sizeof(struct capwap_message_element) + namelength); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + namelength); + element->type = htons(CAPWAP_ELEMENT_WTPNAME); + element->length = htons(namelength); + + dataraw = (struct capwap_wtpname_raw_element*)element->data; + memcpy(&dataraw->name[0], &dataelement->name[0], namelength); + return element; +} + +/* */ +int capwap_wtpname_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_wtpname_element_parsing(struct capwap_message_element* element) { + unsigned short namelength; + struct capwap_wtpname_element* data; + struct capwap_wtpname_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_WTPNAME); + + namelength = ntohs(element->length); + if (!namelength || (namelength > CAPWAP_WTPNAME_MAXLENGTH)) { + return NULL; + } + + /* */ + dataraw = (struct capwap_wtpname_raw_element*)element->data; + data = (struct capwap_wtpname_element*)capwap_alloc(sizeof(struct capwap_wtpname_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + memcpy(&data->name[0], &dataraw->name[0], namelength); + data->name[namelength] = 0; + return data; +} + +/* */ +void capwap_wtpname_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_wtpname.h b/src/common/capwap_element_wtpname.h new file mode 100644 index 0000000..de52f39 --- /dev/null +++ b/src/common/capwap_element_wtpname.h @@ -0,0 +1,24 @@ +#ifndef __CAPWAP_ELEMENT_WTPNAME_HEADER__ +#define __CAPWAP_ELEMENT_WTPNAME_HEADER__ + +#define CAPWAP_ELEMENT_WTPNAME 45 + +#define CAPWAP_WTPNAME_MAXLENGTH 512 + +struct capwap_wtpname_element { + char name[CAPWAP_WTPNAME_MAXLENGTH + 1]; +}; + +struct capwap_message_element* capwap_wtpname_element_create(void* data, unsigned long datalength); +int capwap_wtpname_element_validate(struct capwap_message_element* element); +void* capwap_wtpname_element_parsing(struct capwap_message_element* element); +void capwap_wtpname_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_WTPNAME_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_WTPNAME); \ + f->create(x, sizeof(struct capwap_wtpname_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_WTPNAME_HEADER__ */ diff --git a/src/common/capwap_element_wtprebootstat.c b/src/common/capwap_element_wtprebootstat.c new file mode 100644 index 0000000..d688304 --- /dev/null +++ b/src/common/capwap_element_wtprebootstat.c @@ -0,0 +1,109 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Reboot Count | AC Initiated Count | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Link Failure Count | SW Failure Count | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| HW Failure Count | Other Failure Count | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Unknown Failure Count |Last Failure Ty| ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +Type: 48 for WTP Reboot Statistics +Length: 15 + +********************************************************************/ + +struct capwap_wtprebootstat_raw_element { + unsigned short rebootcount; + unsigned short acinitiatedcount; + unsigned short linkfailurecount; + unsigned short swfailurecount; + unsigned short hwfailurecount; + unsigned short otherfailurecount; + unsigned short unknownfailurecount; + unsigned char lastfailuretype; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_wtprebootstat_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_wtprebootstat_raw_element* dataraw; + struct capwap_wtprebootstat_element* dataelement = (struct capwap_wtprebootstat_element*)data; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_wtprebootstat_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_wtprebootstat_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_wtprebootstat_raw_element)); + element->type = htons(CAPWAP_ELEMENT_WTPREBOOTSTAT); + element->length = htons(sizeof(struct capwap_wtprebootstat_raw_element)); + + dataraw = (struct capwap_wtprebootstat_raw_element*)element->data; + dataraw->rebootcount = htons(dataelement->rebootcount); + dataraw->acinitiatedcount = htons(dataelement->acinitiatedcount); + dataraw->linkfailurecount = htons(dataelement->linkfailurecount); + dataraw->swfailurecount = htons(dataelement->swfailurecount); + dataraw->hwfailurecount = htons(dataelement->hwfailurecount); + dataraw->otherfailurecount = htons(dataelement->otherfailurecount); + dataraw->unknownfailurecount = htons(dataelement->unknownfailurecount); + dataraw->lastfailuretype = dataelement->lastfailuretype; + + return element; +} + +/* */ +int capwap_wtprebootstat_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_wtprebootstat_element_parsing(struct capwap_message_element* element) { + struct capwap_wtprebootstat_element* data; + struct capwap_wtprebootstat_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_WTPREBOOTSTAT); + + if (ntohs(element->length) != sizeof(struct capwap_wtprebootstat_raw_element)) { + return NULL; + } + + /* */ + dataraw = (struct capwap_wtprebootstat_raw_element*)element->data; + data = (struct capwap_wtprebootstat_element*)capwap_alloc(sizeof(struct capwap_wtprebootstat_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + data->rebootcount = ntohs(dataraw->rebootcount); + data->acinitiatedcount = ntohs(dataraw->acinitiatedcount); + data->linkfailurecount = ntohs(dataraw->linkfailurecount); + data->swfailurecount = ntohs(dataraw->swfailurecount); + data->hwfailurecount = ntohs(dataraw->hwfailurecount); + data->otherfailurecount = ntohs(dataraw->otherfailurecount); + data->unknownfailurecount = ntohs(dataraw->unknownfailurecount); + data->lastfailuretype = dataraw->lastfailuretype; + return data; +} + +/* */ +void capwap_wtprebootstat_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_wtprebootstat.h b/src/common/capwap_element_wtprebootstat.h new file mode 100644 index 0000000..38d5ab4 --- /dev/null +++ b/src/common/capwap_element_wtprebootstat.h @@ -0,0 +1,40 @@ +#ifndef __CAPWAP_ELEMENT_WTPREBOOTSTAT_HEADER__ +#define __CAPWAP_ELEMENT_WTPREBOOTSTAT_HEADER__ + +#define CAPWAP_ELEMENT_WTPREBOOTSTAT 48 + +#define CAPWAP_NOTAVAILABLE_REBOOT_COUNT 65535 +#define CAPWAP_NOTAVAILABLE_ACINIT_COUNT 65535 + +#define CAPWAP_LAST_FAILURE_NOTSUPPORTED 0 +#define CAPWAP_LAST_FAILURE_ACINITIATED 1 +#define CAPWAP_LAST_FAILURE_LINK 2 +#define CAPWAP_LAST_FAILURE_SOFTWARE 3 +#define CAPWAP_LAST_FAILURE_HARDWARE 4 +#define CAPWAP_LAST_FAILURE_OTHER 5 +#define CAPWAP_LAST_FAILURE_UNKNOWN 255 + +struct capwap_wtprebootstat_element { + unsigned short rebootcount; + unsigned short acinitiatedcount; + unsigned short linkfailurecount; + unsigned short swfailurecount; + unsigned short hwfailurecount; + unsigned short otherfailurecount; + unsigned short unknownfailurecount; + unsigned char lastfailuretype; +}; + +struct capwap_message_element* capwap_wtprebootstat_element_create(void* data, unsigned long datalength); +int capwap_wtprebootstat_element_validate(struct capwap_message_element* element); +void* capwap_wtprebootstat_element_parsing(struct capwap_message_element* element); +void capwap_wtprebootstat_element_free(void* data); + + +/* Helper */ +#define CAPWAP_CREATE_WTPREBOOTSTAT_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_WTPREBOOTSTAT); \ + f->create(x, sizeof(struct capwap_wtprebootstat_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_WTPREBOOTSTAT_HEADER__ */ diff --git a/src/common/capwap_element_wtpstaticipaddress.c b/src/common/capwap_element_wtpstaticipaddress.c new file mode 100644 index 0000000..4ca4956 --- /dev/null +++ b/src/common/capwap_element_wtpstaticipaddress.c @@ -0,0 +1,98 @@ +#include "capwap.h" +#include "capwap_element.h" + +/******************************************************************** + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| IP Address | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Netmask | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Gateway | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Static | ++-+-+-+-+-+-+-+-+ + +Type: 49 for WTP Static IP Address Information +Length: 13 + +********************************************************************/ + +struct capwap_wtpstaticipaddress_raw_element { + unsigned long address; + unsigned long netmask; + unsigned long gateway; + unsigned char staticip; +} __attribute__((__packed__)); + +/* */ +struct capwap_message_element* capwap_wtpstaticipaddress_element_create(void* data, unsigned long datalength) { + struct capwap_message_element* element; + struct capwap_wtpstaticipaddress_element* dataelement = (struct capwap_wtpstaticipaddress_element*)data; + struct capwap_wtpstaticipaddress_raw_element* dataraw; + + ASSERT(data != NULL); + ASSERT(datalength == sizeof(struct capwap_wtpstaticipaddress_element)); + + /* Alloc block of memory */ + element = capwap_alloc(sizeof(struct capwap_message_element) + sizeof(struct capwap_wtpstaticipaddress_raw_element)); + if (!element) { + capwap_outofmemory(); + } + + /* Create message element */ + memset(element, 0, sizeof(struct capwap_message_element) + sizeof(struct capwap_wtpstaticipaddress_raw_element)); + element->type = htons(CAPWAP_ELEMENT_WTPSTATICIPADDRESS); + element->length = htons(sizeof(struct capwap_wtpstaticipaddress_raw_element)); + + dataraw = (struct capwap_wtpstaticipaddress_raw_element*)element->data; + dataraw->address = dataelement->address.s_addr; + dataraw->netmask = dataelement->netmask.s_addr; + dataraw->gateway = dataelement->gateway.s_addr; + dataraw->staticip = dataelement->staticip; + + return element; +} + +/* */ +int capwap_wtpstaticipaddress_element_validate(struct capwap_message_element* element) { + /* TODO */ + return 1; +} + +/* */ +void* capwap_wtpstaticipaddress_element_parsing(struct capwap_message_element* element) { + struct capwap_wtpstaticipaddress_element* data; + struct capwap_wtpstaticipaddress_raw_element* dataraw; + + ASSERT(element); + ASSERT(ntohs(element->type) == CAPWAP_ELEMENT_WTPSTATICIPADDRESS); + + if (ntohs(element->length) != sizeof(struct capwap_wtpstaticipaddress_raw_element)) { + return NULL; + } + + /* */ + data = (struct capwap_wtpstaticipaddress_element*)capwap_alloc(sizeof(struct capwap_wtpstaticipaddress_element)); + if (!data) { + capwap_outofmemory(); + } + + /* */ + dataraw = (struct capwap_wtpstaticipaddress_raw_element*)element->data; + data->address.s_addr = dataraw->address; + data->netmask.s_addr = dataraw->netmask; + data->gateway.s_addr = dataraw->gateway; + data->staticip = dataraw->staticip; + + return data; +} + +/* */ +void capwap_wtpstaticipaddress_element_free(void* data) { + ASSERT(data != NULL); + + capwap_free(data); +} diff --git a/src/common/capwap_element_wtpstaticipaddress.h b/src/common/capwap_element_wtpstaticipaddress.h new file mode 100644 index 0000000..05a622d --- /dev/null +++ b/src/common/capwap_element_wtpstaticipaddress.h @@ -0,0 +1,24 @@ +#ifndef __CAPWAP_ELEMENT_WTPSTATICIPADDRESS_HEADER__ +#define __CAPWAP_ELEMENT_WTPSTATICIPADDRESS_HEADER__ + +#define CAPWAP_ELEMENT_WTPSTATICIPADDRESS 49 + +struct capwap_wtpstaticipaddress_element { + struct in_addr address; + struct in_addr netmask; + struct in_addr gateway; + unsigned char staticip; +}; + +struct capwap_message_element* capwap_wtpstaticipaddress_element_create(void* data, unsigned long length); +int capwap_wtpstaticipaddress_element_validate(struct capwap_message_element* element); +void* capwap_wtpstaticipaddress_element_parsing(struct capwap_message_element* element); +void capwap_wtpstaticipaddress_element_free(void* data); + +/* Helper */ +#define CAPWAP_CREATE_WTPSTATICIPADDRESS_ELEMENT(x) ({ \ + struct capwap_message_elements_func* f = capwap_get_message_element(CAPWAP_ELEMENT_WTPSTATICIPADDRESS); \ + f->create(x, sizeof(struct capwap_wtpstaticipaddress_element)); \ + }) + +#endif /* __CAPWAP_ELEMENT_WTPSTATICIPADDRESS_HEADER__ */ diff --git a/src/common/capwap_error.h b/src/common/capwap_error.h new file mode 100644 index 0000000..28ac124 --- /dev/null +++ b/src/common/capwap_error.h @@ -0,0 +1,10 @@ +#ifndef __CAPWAP_ERROR_HEADER__ +#define __CAPWAP_ERROR_HEADER__ + +#define CAPWAP_SUCCESSFUL 0 +#define CAPWAP_ASSERT_CONDITION -1 +#define CAPWAP_OUT_OF_MEMORY -2 +#define CAPWAP_REQUEST_ROOT -3 +#define CAPWAP_CRYPT_ERROR -4 + +#endif /* __CAPWAP_ERROR_HEADER__*/ diff --git a/src/common/capwap_event.c b/src/common/capwap_event.c new file mode 100644 index 0000000..629710a --- /dev/null +++ b/src/common/capwap_event.c @@ -0,0 +1,99 @@ +#include "capwap.h" +#include "capwap_event.h" + +#ifndef CAPWAP_MULTITHREADING_ENABLE +#error "Warning: multithreading is disabled\n" +#endif + +/* */ +int capwap_event_init(capwap_event_t* e) { + ASSERT(e != NULL); + + e->set = 0; + + if (pthread_cond_init(&e->event, NULL) != 0) { + return 0; + } + + if (pthread_mutex_init(&e->mutex, NULL) != 0) { + pthread_cond_destroy(&e->event); + return 0; + } + + return 1; +} + +/* */ +void capwap_event_destroy(capwap_event_t* e) { + ASSERT(e != NULL); + + pthread_cond_destroy(&e->event); + pthread_mutex_destroy(&e->mutex); +} + +/* */ +void capwap_event_signal(capwap_event_t* e) { + ASSERT(e != NULL); + + pthread_mutex_lock(&e->mutex); + + e->set = 1; + pthread_cond_signal(&e->event); + + pthread_mutex_unlock(&e->mutex); +} + +/* */ +void capwap_event_reset(capwap_event_t* e) { + ASSERT(e != NULL); + + pthread_mutex_lock(&e->mutex); + + e->set = 0; + + pthread_mutex_unlock(&e->mutex); +} + +/* */ +void capwap_event_wait(capwap_event_t* e) { + capwap_event_wait_timeout(e, -1); +} + +/* */ +int capwap_event_wait_timeout(capwap_event_t* e, long timeout) { + int result = 0; + + ASSERT(e != NULL); + + pthread_mutex_lock(&e->mutex); + + if (e->set) { + result = 1; + } else if (timeout < 0) { + if (!pthread_cond_wait(&e->event, &e->mutex)) { + result = 1; + } + } else { + struct timeval tp; + + gettimeofday(&tp, NULL); + tp.tv_sec += timeout / 1000; + tp.tv_usec += ((timeout % 1000) * 1000); + if (tp.tv_usec > 1000000) { + tp.tv_sec++; + tp.tv_usec -= 1000000; + } + + struct timespec ts; + ts.tv_sec = tp.tv_sec; + ts.tv_nsec = tp.tv_usec * 1000; + if (!pthread_cond_timedwait(&e->event, &e->mutex, &ts)) { + result = 1; + } + } + + e->set = 0; + pthread_mutex_unlock(&e->mutex); + + return result; +} diff --git a/src/common/capwap_event.h b/src/common/capwap_event.h new file mode 100644 index 0000000..b404295 --- /dev/null +++ b/src/common/capwap_event.h @@ -0,0 +1,23 @@ +#ifndef __CAPWAP_EVENT_HEADER__ +#define __CAPWAP_EVENT_HEADER__ + +#ifdef CAPWAP_MULTITHREADING_ENABLE + +#include + +typedef struct { + char set; + pthread_cond_t event; + pthread_mutex_t mutex; +} capwap_event_t; + +int capwap_event_init(capwap_event_t* e); +void capwap_event_destroy(capwap_event_t* e); +void capwap_event_signal(capwap_event_t* e); +void capwap_event_reset(capwap_event_t* e); +void capwap_event_wait(capwap_event_t* e); +int capwap_event_wait_timeout(capwap_event_t* e, long timeout); + +#endif /* CAPWAP_MULTITHREADING_ENABLE */ + +#endif /* __CAPWAP_EVENT_HEADER__ */ diff --git a/src/common/capwap_list.c b/src/common/capwap_list.c new file mode 100644 index 0000000..b811fb0 --- /dev/null +++ b/src/common/capwap_list.c @@ -0,0 +1,190 @@ +#include "capwap.h" +#include "capwap_list.h" + +/* */ +struct capwap_list* capwap_list_create(void) { + struct capwap_list* list; + + list = (struct capwap_list*)capwap_alloc(sizeof(struct capwap_list)); + if (!list) { + capwap_outofmemory(); + } + + memset(list, 0, sizeof(struct capwap_list)); + return list; +} + +/* */ +void capwap_list_free(struct capwap_list* list) { + ASSERT(list != NULL); + + capwap_list_flush(list); + capwap_free(list); +} + +/* */ +void capwap_list_flush(struct capwap_list* list) { + struct capwap_list_item* item; + struct capwap_list_item* next; + + ASSERT(list != NULL); + + item = list->first; + while (item) { + next = item->next; + capwap_itemlist_free(item); + item = next; + } + + list->first = NULL; + list->last = NULL; + list->count = 0; +} + +/* */ +struct capwap_list_item* capwap_itemlist_create_with_item(void* item, int size) { + struct capwap_list_item* itemlist; + + itemlist = (struct capwap_list_item*)capwap_alloc(sizeof(struct capwap_list_item)); + if (!itemlist) { + capwap_outofmemory(); + } + + memset(itemlist, 0, sizeof(struct capwap_list_item)); + itemlist->item = item; + itemlist->itemsize = size; + itemlist->autodelete = 1; + + return itemlist; +} + +/* */ +struct capwap_list_item* capwap_itemlist_create(int size) { + void* item; + + ASSERT(size > 0); + + item = capwap_alloc(size); + if (!item) { + capwap_outofmemory(); + } + + return capwap_itemlist_create_with_item(item, size); +} + +/* */ +void capwap_itemlist_free(struct capwap_list_item* item) { + ASSERT(item != NULL); + ASSERT(item->item != NULL); + + if (item->autodelete) { + capwap_free(item->item); + } + + capwap_free(item); +} + +/* */ +struct capwap_list_item* capwap_itemlist_remove(struct capwap_list* list, struct capwap_list_item* item) { + ASSERT(list != NULL); + ASSERT(item != NULL); + + if (item->prev) { + item->prev->next = item->next; + } else { + list->first = item->next; + } + + if (item->next) { + item->next->prev = item->prev; + } else { + list->last = item->prev; + } + + item->next = NULL; + item->prev = NULL; + list->count--; + + return item; +} + +/* */ +struct capwap_list_item* capwap_itemlist_remove_head(struct capwap_list* list) { + struct capwap_list_item* item; + + ASSERT(list != NULL); + + item = list->first; + if (item != NULL) { + list->first = item->next; + if (list->first) { + list->first->prev = NULL; + } else { + list->last = NULL; + } + + item->next = NULL; + item->prev = NULL; + list->count--; + } + + return item; +} + +/* */ +void capwap_itemlist_insert_before(struct capwap_list* list, struct capwap_list_item* before, struct capwap_list_item* item) { + ASSERT(list != NULL); + ASSERT(item != NULL); + + list->count++; + + if (!before) { + if (list->first) { + before = list->first; + } else { + list->first = item; + list->last = item; + item->next = NULL; + item->prev = NULL; + return; + } + } + + item->prev = before->prev; + item->next = before; + if (!before->prev) { + list->first = item; + } else { + before->prev->next = item; + } + before->prev = item; +} + +/* */ +void capwap_itemlist_insert_after(struct capwap_list* list, struct capwap_list_item* after, struct capwap_list_item* item) { + ASSERT(list != NULL); + ASSERT(item != NULL); + + list->count++; + + if (!after) { + if (list->last) { + after = list->last; + } else { + list->first = item; + list->last = item; + item->next = NULL; + item->prev = NULL; + return; + } + } + + item->prev = after; + item->next = after->next; + if (!after->next) { + list->last = item; + } else { + after->next->prev = item; + } + after->next = item; +} diff --git a/src/common/capwap_list.h b/src/common/capwap_list.h new file mode 100644 index 0000000..ea741de --- /dev/null +++ b/src/common/capwap_list.h @@ -0,0 +1,33 @@ +#ifndef __CAPWAP_LIST_HEADER__ +#define __CAPWAP_LIST_HEADER__ + +/* Item */ +struct capwap_list_item { + void* item; + int itemsize; + int autodelete; + struct capwap_list_item* next; + struct capwap_list_item* prev; +}; + +/* List */ +struct capwap_list { + unsigned long count; + struct capwap_list_item* first; + struct capwap_list_item* last; +}; + +struct capwap_list* capwap_list_create(void); +void capwap_list_free(struct capwap_list* list); +void capwap_list_flush(struct capwap_list* list); + +struct capwap_list_item* capwap_itemlist_create(int size); +struct capwap_list_item* capwap_itemlist_create_with_item(void* item, int size); +void capwap_itemlist_free(struct capwap_list_item* item); + +struct capwap_list_item* capwap_itemlist_remove(struct capwap_list* list, struct capwap_list_item* item); +struct capwap_list_item* capwap_itemlist_remove_head(struct capwap_list* list); +void capwap_itemlist_insert_before(struct capwap_list* list, struct capwap_list_item* before, struct capwap_list_item* item); +void capwap_itemlist_insert_after(struct capwap_list* list, struct capwap_list_item* after, struct capwap_list_item* item); + +#endif /* __CAPWAP_LIST_HEADER__ */ diff --git a/src/common/capwap_lock.c b/src/common/capwap_lock.c new file mode 100644 index 0000000..264e5cd --- /dev/null +++ b/src/common/capwap_lock.c @@ -0,0 +1,41 @@ +#include "capwap.h" +#include "capwap_lock.h" + +#ifndef CAPWAP_MULTITHREADING_ENABLE +#error "Warning: multithreading is disabled\n" +#endif + +/* */ +int capwap_lock_init(capwap_lock_t* lock) { + pthread_mutexattr_t attr; + + ASSERT(lock != NULL); + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + if (pthread_mutex_init(&lock->mutex, &attr) != 0) + return 0; + + return 1; +} + +/* */ +void capwap_lock_destroy(capwap_lock_t* lock) { + ASSERT(lock != NULL); + + pthread_mutex_destroy(&lock->mutex); +} + +/* */ +void capwap_lock_enter(capwap_lock_t* lock) { + ASSERT(lock != NULL); + + pthread_mutex_lock(&lock->mutex); +} + +/* */ +void capwap_lock_exit(capwap_lock_t* lock) { + ASSERT(lock != NULL); + + pthread_mutex_unlock(&lock->mutex); +} diff --git a/src/common/capwap_lock.h b/src/common/capwap_lock.h new file mode 100644 index 0000000..4ef8443 --- /dev/null +++ b/src/common/capwap_lock.h @@ -0,0 +1,19 @@ +#ifndef __CAPWAP_LOCK_HEADER__ +#define __CAPWAP_LOCK_HEADER__ + +#ifdef CAPWAP_MULTITHREADING_ENABLE + +#include "pthread.h" + +typedef struct { + pthread_mutex_t mutex; +} capwap_lock_t; + +int capwap_lock_init(capwap_lock_t* lock); +void capwap_lock_destroy(capwap_lock_t* lock); +void capwap_lock_enter(capwap_lock_t* lock); +void capwap_lock_exit(capwap_lock_t* lock); + +#endif /* CAPWAP_MULTITHREADING_ENABLE */ + +#endif /* __CAPWAP_LOCK_HEADER__ */ diff --git a/src/common/capwap_logging.c b/src/common/capwap_logging.c new file mode 100644 index 0000000..63af04e --- /dev/null +++ b/src/common/capwap_logging.c @@ -0,0 +1,115 @@ +#include "capwap.h" +#include "capwap_logging.h" + +#ifdef CAPWAP_MULTITHREADING_ENABLE +#include "capwap_lock.h" + +static capwap_lock_t l_loglock; +#endif + +/* */ +static int logginglevel = CAPWAP_LOGGING_NONE; +static int loggingoutputstdout = 0; +static int loggingoutputstderr = 0; + +/* */ +static char logginglevelid[] = { 'N', 'F', 'E', 'W', 'I', 'D' }; + +/* */ +static void prefix_logging(int level, char* buffer) { + time_t timenow; + struct tm* tmnow; + + time(&timenow); + tmnow = localtime(&timenow); + sprintf(buffer, "[%02d/%02d/%04d %02d:%02d:%02d] <%c> ", tmnow->tm_mday, tmnow->tm_mon + 1, tmnow->tm_year + 1900, tmnow->tm_hour, tmnow->tm_min, tmnow->tm_sec, logginglevelid[level]); +} + +/* */ +void capwap_logging_init() { +#ifdef CAPWAP_MULTITHREADING_ENABLE + capwap_lock_init(&l_loglock); +#endif +} + +/* */ +void capwap_logging_close() { +#ifdef CAPWAP_MULTITHREADING_ENABLE + capwap_lock_destroy(&l_loglock); +#endif +} + +/* */ +void capwap_logging_verboselevel(unsigned int level) { +#ifdef CAPWAP_MULTITHREADING_ENABLE + capwap_lock_enter(&l_loglock); +#endif + + logginglevel = level; + +#ifdef CAPWAP_MULTITHREADING_ENABLE + capwap_lock_exit(&l_loglock); +#endif +} + +/* */ +void capwap_logging_disable_allinterface() { +#ifdef CAPWAP_MULTITHREADING_ENABLE + capwap_lock_enter(&l_loglock); +#endif + + loggingoutputstdout = 0; + loggingoutputstderr = 0; + +#ifdef CAPWAP_MULTITHREADING_ENABLE + capwap_lock_exit(&l_loglock); +#endif +} + +/* */ +void capwap_logging_enable_console(int error) { +#ifdef CAPWAP_MULTITHREADING_ENABLE + capwap_lock_enter(&l_loglock); +#endif + + /* Enable only one of stdout/stderr */ + loggingoutputstdout = (error ? 0 : 1); + loggingoutputstderr = !loggingoutputstdout; + +#ifdef CAPWAP_MULTITHREADING_ENABLE + capwap_lock_exit(&l_loglock); +#endif +} + +/* */ +#ifdef ENABLE_LOGGING +void capwap_logging_printf(int level, const char* format, ...) { + va_list args; + char prefix[256]; + + va_start(args, format); + +#ifdef CAPWAP_MULTITHREADING_ENABLE + capwap_lock_enter(&l_loglock); +#endif + + if ((logginglevel != CAPWAP_LOGGING_NONE) && (level <= logginglevel)) { + prefix_logging(level, prefix); + + if (loggingoutputstdout || loggingoutputstderr) { + FILE* output = (loggingoutputstdout ? stdout : stderr); + + fprintf(output, prefix); + vfprintf(output, format, args); + fprintf(output, "\n"); + fflush(output); + } + } + +#ifdef CAPWAP_MULTITHREADING_ENABLE + capwap_lock_exit(&l_loglock); +#endif + + va_end(args); +} +#endif diff --git a/src/common/capwap_logging.h b/src/common/capwap_logging.h new file mode 100644 index 0000000..71e95ce --- /dev/null +++ b/src/common/capwap_logging.h @@ -0,0 +1,37 @@ +#ifndef __CAPWAP_LOGGING_HEADER__ +#define __CAPWAP_LOGGING_HEADER__ + +/* Logging level */ +#define CAPWAP_LOGGING_NONE 0 +#define CAPWAP_LOGGING_FATAL 1 +#define CAPWAP_LOGGING_ERROR 2 +#define CAPWAP_LOGGING_WARNING 3 +#define CAPWAP_LOGGING_INFO 4 +#define CAPWAP_LOGGING_DEBUG 5 + +/* Logging initialize function */ +void capwap_logging_init(); +void capwap_logging_close(); + +/* */ +void capwap_logging_verboselevel(unsigned int level); + +/* */ +void capwap_logging_disable_allinterface(); +void capwap_logging_enable_console(int error); + +/* */ +#ifdef ENABLE_LOGGING +void capwap_logging_printf(int level, const char *format, ...); +#else +#define capwap_logging_printf(l, f, args...) (0) +#endif + +/* */ +#define capwap_logging_fatal(f, args...) capwap_logging_printf(CAPWAP_LOGGING_FATAL, f, ##args) +#define capwap_logging_error(f, args...) capwap_logging_printf(CAPWAP_LOGGING_ERROR, f, ##args) +#define capwap_logging_warning(f, args...) capwap_logging_printf(CAPWAP_LOGGING_WARNING, f, ##args) +#define capwap_logging_info(f, args...) capwap_logging_printf(CAPWAP_LOGGING_INFO, f, ##args) +#define capwap_logging_debug(f, args...) capwap_logging_printf(CAPWAP_LOGGING_DEBUG, f, ##args) + +#endif /* __CAPWAP_LOGGING_HEADER__ */ diff --git a/src/common/capwap_network.c b/src/common/capwap_network.c new file mode 100644 index 0000000..8e23e30 --- /dev/null +++ b/src/common/capwap_network.c @@ -0,0 +1,1240 @@ +#include "capwap.h" +#include "capwap_array.h" +#include "capwap_network.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CAPWAP_ROUTE_NOT_FOUND 0 +#define CAPWAP_ROUTE_LOCAL_ADDRESS 1 +#define CAPWAP_ROUTE_VIA_ADDRESS 2 + +#ifndef IPV6_RECVPKTINFO +#define IPV6_RECVPKTINFO IPV6_PKTINFO +#endif + +/* Prepare socket to bind */ +static int capwap_configure_socket(int sock, int socketfamily, int socketprotocol, int usebroadcast, char* bindinterface, int flags) { + int flag; + + ASSERT(sock >= 0); + ASSERT((socketfamily == AF_INET) || (socketfamily == AF_INET6)); + ASSERT((socketprotocol == IPPROTO_UDP) || (socketprotocol == IPPROTO_UDPLITE)); + + /* Set correct value for IPv6 if dualstack is disabled */ + if ((socketfamily == AF_INET6) && ((flags & CAPWAP_IPV6ONLY_FLAG) != 0)) { + int on = 1; + int result; + + usebroadcast = 0; + result = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&on, sizeof(int)); + if (result) { + capwap_logging_debug("Unable set IPV6_V6ONLY to socket"); + return -1; + } + } + + /* Reuse address */ + flag = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int))) { + capwap_logging_debug("Unable set SO_REUSEADDR to socket"); + return -1; + } + + /* Broadcast */ + if (usebroadcast) { + flag = 1; + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(int))) { + capwap_logging_debug("Unable set SO_BROADCAST to socket"); + return -1; + } + } + + /* Bind to interface */ + if ((bindinterface != NULL) && (bindinterface[0] != 0)) { + if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, bindinterface, strlen(bindinterface) + 1)) { + capwap_logging_debug("Unable set SO_BINDTODEVICE to socket %d", errno); + return -1; + } + } + + /* Disable checksum */ + if (socketprotocol == IPPROTO_UDPLITE) { + flag = 8; + if (setsockopt(sock, SOL_UDPLITE, UDPLITE_SEND_CSCOV, &flag, sizeof(int))) { + capwap_logging_debug("Unable set UDPLITE_SEND_CSCOV to socket"); + return -1; + } + } else if (socketfamily == AF_INET) { + flag = 1; + if (setsockopt(sock, SOL_SOCKET, SO_NO_CHECK, &flag, sizeof(int))) { + capwap_logging_debug("Unable set SO_NO_CHECK to socket"); + return -1; + } + } + + /* Retrieve information into sendto/recvfrom local address */ + if (socketfamily == AF_INET) { +#ifdef IP_PKTINFO + flag = 1; + if (setsockopt(sock, SOL_IP, IP_PKTINFO, &flag, sizeof(int))) { + capwap_logging_debug("Unable set IP_PKTINFO to socket '%d'", errno); + return -1; + } +#elif defined IP_RECVDSTADDR + flag = 1; + if (setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &flag, sizeof(int))) { + capwap_logging_debug("Unable set IP_RECVDSTADDR to socket '%d'", errno); + return -1; + } +#else + #error "No method of getting the destination ip address supported" +#endif + } else if (socketfamily == AF_INET6) { + flag = 1; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &flag, sizeof(int))) { + capwap_logging_debug("Unable set IPV6_RECVPKTINFO to socket '%d'", errno); + return -1; + } + } + + return 0; +} + +/* */ +static void capwap_save_socket(struct capwap_network* net, int socketfamily, int socketprotocol, int sock_ctrl, int sock_data) { + int index; + + ASSERT(net != NULL); + ASSERT((socketfamily == AF_INET) || (socketfamily == AF_INET6)); + ASSERT((socketprotocol == IPPROTO_UDP) || (socketprotocol == IPPROTO_UDPLITE)); + ASSERT(sock_ctrl >= 0); + ASSERT(sock_data >= 0); + + if (socketfamily == AF_INET) { + if (socketprotocol == IPPROTO_UDP) { + index = CAPWAP_SOCKET_IPV4_UDP; + } else if (socketprotocol == IPPROTO_UDPLITE) { + index = CAPWAP_SOCKET_IPV4_UDPLITE; + } + } else if (socketfamily == AF_INET6) { + if (socketprotocol == IPPROTO_UDP) { + index = CAPWAP_SOCKET_IPV6_UDP; + } else if (socketprotocol == IPPROTO_UDPLITE) { + index = CAPWAP_SOCKET_IPV6_UDPLITE; + } + } + + net->sock_ctrl[index] = sock_ctrl; + net->sock_data[index] = sock_data; +} + +/* Listen socket */ +static int capwap_prepare_bind_socket(struct capwap_network* net, int socketfamily, int socketprotocol) { + int result = 0; + int sock_ctrl = -1; + int sock_data = -1; + struct sockaddr_storage bindaddr; + + ASSERT(net != NULL); + ASSERT(net->bind_sock_ctrl_port != 0); + ASSERT((socketfamily == AF_INET) || (socketfamily == AF_INET6)); + ASSERT((socketprotocol == IPPROTO_UDP) || (socketprotocol == IPPROTO_UDPLITE)); + + /* */ + memset(&bindaddr, 0, sizeof(struct sockaddr_storage)); + bindaddr.ss_family = socketfamily; + if (socketfamily == AF_INET) { + ((struct sockaddr_in*)(&bindaddr))->sin_addr.s_addr = INADDR_ANY; + } else if (socketfamily == AF_INET6) { + memset(&((struct sockaddr_in6*)(&bindaddr))->sin6_addr, 0, sizeof(struct in6_addr)); + } + + /* Control socket */ + sock_ctrl = socket(socketfamily, SOCK_DGRAM, socketprotocol); + if (sock_ctrl >= 0) { + if (!capwap_configure_socket(sock_ctrl, socketfamily, socketprotocol, 1, net->bind_interface, net->bind_ctrl_flags)) { + /* Bind address */ + CAPWAP_SET_NETWORK_PORT(&bindaddr, net->bind_sock_ctrl_port); + if (!bind(sock_ctrl, (struct sockaddr*)&bindaddr, sizeof(struct sockaddr_storage))) { + /* Data socket */ + sock_data = socket(socketfamily, SOCK_DGRAM, socketprotocol); + if (sock_data >= 0) { + if (!capwap_configure_socket(sock_data, socketfamily, socketprotocol, 0, net->bind_interface, net->bind_data_flags)) { + /* Bind address */ + CAPWAP_SET_NETWORK_PORT(&bindaddr, net->bind_sock_ctrl_port + 1); + if (!bind(sock_data, (struct sockaddr*)&bindaddr, sizeof(struct sockaddr_storage))) { + result = 1; + capwap_save_socket(net, socketfamily, socketprotocol, sock_ctrl, sock_data); + } else { + close(sock_data); + close(sock_ctrl); + } + } else { + close(sock_data); + close(sock_ctrl); + } + } else { + close(sock_ctrl); + } + } else { + close(sock_ctrl); + } + } else { + close(sock_ctrl); + } + } + + + return result; +} + +/* */ +int capwap_bind_sockets(struct capwap_network* net) { + int bindipv4 = 1; + + ASSERT(net != NULL); + + if ((net->sock_family == AF_UNSPEC) || (net->sock_family == AF_INET6)) { + /* UDP protocol */ + if (!capwap_prepare_bind_socket(net, AF_INET6, IPPROTO_UDP)) { + return 0; + } + + /* UDPLITE protocol */ + if (!capwap_prepare_bind_socket(net, AF_INET6, IPPROTO_UDPLITE)) { + return 0; + } + + /* Verify can use dual stack protocol */ + if ((net->bind_ctrl_flags & CAPWAP_IPV6ONLY_FLAG) == 0) { + bindipv4 = 0; + } + } + + if (bindipv4 && ((net->sock_family == AF_UNSPEC) || (net->sock_family == AF_INET))) { + if (!capwap_prepare_bind_socket(net, AF_INET, IPPROTO_UDP)) { + return 0; + } + + if (!capwap_prepare_bind_socket(net, AF_INET, IPPROTO_UDPLITE)) { + return 0; + } + } + + return 1; +} + +/* Get socket */ +int capwap_get_socket(struct capwap_network* net, int socketfamily, int socketprotocol, int isctrlsocket) { + int index; + + ASSERT(net != NULL); + ASSERT((socketfamily == AF_INET) || (socketfamily == AF_INET6)); + ASSERT((socketprotocol == IPPROTO_UDP) || (socketprotocol == IPPROTO_UDPLITE)); + + if (socketfamily == AF_INET) { + if (socketprotocol == IPPROTO_UDP) { + index = CAPWAP_SOCKET_IPV4_UDP; + } else if (socketprotocol == IPPROTO_UDPLITE) { + index = CAPWAP_SOCKET_IPV4_UDPLITE; + } + } else if (socketfamily == AF_INET6) { + if (socketprotocol == IPPROTO_UDP) { + index = CAPWAP_SOCKET_IPV6_UDP; + } else if (socketprotocol == IPPROTO_UDPLITE) { + index = CAPWAP_SOCKET_IPV6_UDPLITE; + } + } + + return (isctrlsocket ? net->sock_ctrl[index] : net->sock_data[index]); +} + +/* Close socket */ +void capwap_close_sockets(struct capwap_network* net) { + int i; + + ASSERT(net != NULL); + + for (i = 0; i < CAPWAP_MAX_SOCKETS; i++) { + if (net->sock_ctrl[i] >= 0) { + shutdown(net->sock_ctrl[i], SHUT_RDWR); + close(net->sock_ctrl[i]); + net->sock_ctrl[i] = -1; + } + + if (net->sock_data[i] >= 0) { + shutdown(net->sock_data[i], SHUT_RDWR); + close(net->sock_data[i]); + net->sock_data[i] = -1; + } + } +} + +/* Compare ip address */ +int capwap_compare_ip(struct sockaddr_storage* addr1, struct sockaddr_storage* addr2) { + ASSERT(addr1 != NULL); + ASSERT(addr2 != NULL); + + if (addr1->ss_family == addr2->ss_family) { + if (addr1->ss_family == AF_INET) { + struct sockaddr_in* addr1_in = (struct sockaddr_in*)addr1; + struct sockaddr_in* addr2_in = (struct sockaddr_in*)addr2; + + if (addr1_in->sin_addr.s_addr == addr2_in->sin_addr.s_addr) { + if (addr1_in->sin_port == addr2_in->sin_port) { + return 0; + } + } + } else if (addr1->ss_family == AF_INET6) { + struct sockaddr_in6* addr1_in6 = (struct sockaddr_in6*)addr1; + struct sockaddr_in6* addr2_in6 = (struct sockaddr_in6*)addr2; + + if (!memcmp(&addr1_in6->sin6_addr, &addr2_in6->sin6_addr, sizeof(struct in6_addr))) { + if (addr1_in6->sin6_port == addr2_in6->sin6_port) { + return 0; + } + } + } + } + + return 1; +} + +/* Receive packet with timeout */ +int capwap_recvfrom(struct pollfd* fds, int fdscount, void* buffer, int* size, struct sockaddr_storage* recvfromaddr, struct sockaddr_storage* recvtoaddr, struct timeout_control* timeout) { + int i; + int polltimeout = -1; + int readysocket; + int result = CAPWAP_RECV_ERROR_SOCKET; + + ASSERT(fds); + ASSERT(fdscount > 0); + ASSERT(buffer != NULL); + ASSERT(size != NULL); + ASSERT(*size > 0); + ASSERT(recvfromaddr != NULL); + ASSERT(recvtoaddr != NULL); + + memset(recvfromaddr, 0, sizeof(struct sockaddr_storage)); + if (recvtoaddr) { + memset(recvtoaddr, 0, sizeof(struct sockaddr_storage)); + } + + /* Check timeout */ + if (timeout) { + long indextimer; + + capwap_update_timeout(timeout); + polltimeout = capwap_get_timeout(timeout, &indextimer); + if ((polltimeout <= 0) && (indextimer != CAPWAP_TIMER_UNDEF)) { + return CAPWAP_RECV_ERROR_TIMEOUT; + } + } + + for (i = 0; i < fdscount; i++) { + fds[i].revents = 0; + } + + /* Wait event */ + readysocket = poll(fds, fdscount, polltimeout); + if (readysocket > 0) { + /* Get packet from only one socket */ + for (i = 0; i < fdscount; i++) { + if ((fds[i].revents & POLLIN) != 0) { + int packetsize = -1; + socklen_t sendaddresslen = sizeof(struct sockaddr_storage); + struct sockaddr_storage sockinfo; + socklen_t sockinfolen = sizeof(struct sockaddr_storage); + struct iovec iov; + struct msghdr msgh; + struct cmsghdr* cmsg; + char cbuf[256]; + + /* Information socket */ + memset(&sockinfo, 0, sizeof(struct sockaddr_storage)); + if (getsockname(fds[i].fd, (struct sockaddr*)&sockinfo, &sockinfolen) < 0) { + break; + } + + iov.iov_base = buffer; + iov.iov_len = *size; + + memset(&msgh, 0, sizeof(struct msghdr)); + msgh.msg_control = cbuf; + msgh.msg_controllen = sizeof(cbuf); + msgh.msg_name = recvfromaddr; + msgh.msg_namelen = sendaddresslen; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_flags = 0; + + /* Receive packet with recvmsg */ + do { + packetsize = recvmsg(fds[i].fd, &msgh, 0); + } while ((packetsize < 0) && ((errno == EAGAIN) || (errno == EINTR))); + + if (packetsize > 0) { + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh, cmsg)) { +#ifdef IP_PKTINFO + if ((cmsg->cmsg_level == SOL_IP) && (cmsg->cmsg_type == IP_PKTINFO)) { + struct in_pktinfo* i = (struct in_pktinfo*)CMSG_DATA(cmsg); + struct sockaddr_in* addr = (struct sockaddr_in*)recvtoaddr; + + addr->sin_family = AF_INET; + memcpy(&addr->sin_addr, &i->ipi_addr, sizeof(struct in_addr)); + addr->sin_port = ((struct sockaddr_in*)&sockinfo)->sin_port; + + break; + } +#elif defined IP_RECVDSTADDR + if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) { + struct in_addr* i = (struct in_addr*)CMSG_DATA(cmsg); + struct sockaddr_in* addr = (struct sockaddr_in*)recvtoaddr; + + addr->sin_family = AF_INET; + memcpy(&addr->sin_addr, i, sizeof(struct in_addr)); + addr->sin_port = ((struct sockaddr_in*)&sockinfo)->sin_port; + + break; + } +#else + #error "No method of getting the destination ip address supported" +#endif + if ((cmsg->cmsg_level == IPPROTO_IPV6) && ((cmsg->cmsg_type == IPV6_PKTINFO) || (cmsg->cmsg_type == IPV6_RECVPKTINFO))) { + struct in6_pktinfo* i = (struct in6_pktinfo*)CMSG_DATA(cmsg); + struct sockaddr_in6* addr = (struct sockaddr_in6*)recvtoaddr; + + addr->sin6_family = AF_INET6; + memcpy(&addr->sin6_addr, &i->ipi6_addr, sizeof(struct in6_addr)); + addr->sin6_port = ((struct sockaddr_in6*)&sockinfo)->sin6_port; + + break; + } + } + } else if (packetsize < 0) { + break; + } + + *size = packetsize; + result = i; + + break; + } else if ((fds[i].revents & (POLLHUP | POLLERR | POLLNVAL)) != 0) { + break; + } + } + } else if (readysocket == 0) { + result = CAPWAP_RECV_ERROR_TIMEOUT; + if (timeout) { + /* Update timer for detect timeout */ + capwap_update_timeout(timeout); + } + } else { + if (errno == EINTR) { + result = CAPWAP_RECV_ERROR_INTR; + } + } + + return result; +} + +/* */ +void capwap_network_init(struct capwap_network* net) { + int i; + + ASSERT(net != NULL); + + net->sock_family = AF_UNSPEC; + net->bind_sock_ctrl_port = CAPWAP_CONTROL_PORT; + for (i = 0; i < CAPWAP_MAX_SOCKETS; i++) { + net->sock_ctrl[i] = -1; + net->sock_data[i] = -1; + } +} + +/* */ +int capwap_network_set_pollfd(struct capwap_network* net, struct pollfd* fds, int fdscount) { + int i; + int j; + int count = 0; + + ASSERT(net != NULL); + ASSERT(fds != NULL); + ASSERT(fdscount > 0); + + /* Count the socket */ + for (i = 0; i < CAPWAP_MAX_SOCKETS; i++) { + if (net->sock_ctrl[i] >= 0) { + ASSERT(net->sock_data[i] >= 0); + count++; + } + } + + /* Check size of fds array */ + if (fdscount < (count * 2)) { + return -1; + } + + /* Configure fds array */ + for (i = 0, j = 0; i < CAPWAP_MAX_SOCKETS; i++) { + if (net->sock_ctrl[i] >= 0) { + fds[j].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; + fds[j].fd = net->sock_ctrl[i]; + fds[j + count].events = POLLIN | POLLERR | POLLHUP | POLLNVAL; + fds[j + count].fd = net->sock_data[i]; + + j++; + } + } + + return (count * 2); +} + +/* */ +void capwap_get_network_socket(struct capwap_network* net, struct capwap_socket* sock, int fd) { + int i; + + ASSERT(net != NULL); + ASSERT(sock != NULL); + ASSERT(fd >= 0); + + for (i = 0; i < CAPWAP_MAX_SOCKETS; i++) { + if (net->sock_ctrl[i] == fd) { + sock->isctrlsocket = 1; + switch (i) { + case CAPWAP_SOCKET_IPV4_UDP: { + sock->family = AF_INET; + sock->type = CAPWAP_SOCKET_UDP; + sock->socket[CAPWAP_SOCKET_UDP] = net->sock_ctrl[i]; + sock->socket[CAPWAP_SOCKET_UDPLITE] = net->sock_ctrl[i + 1]; + break; + } + + case CAPWAP_SOCKET_IPV4_UDPLITE: { + sock->family = AF_INET; + sock->type = CAPWAP_SOCKET_UDPLITE; + sock->socket[CAPWAP_SOCKET_UDP] = net->sock_ctrl[i - 1]; + sock->socket[CAPWAP_SOCKET_UDPLITE] = net->sock_ctrl[i]; + break; + } + + case CAPWAP_SOCKET_IPV6_UDP: { + sock->family = AF_INET6; + sock->type = CAPWAP_SOCKET_UDP; + sock->socket[CAPWAP_SOCKET_UDP] = net->sock_ctrl[i]; + sock->socket[CAPWAP_SOCKET_UDPLITE] = net->sock_ctrl[i + 1]; + break; + } + + case CAPWAP_SOCKET_IPV6_UDPLITE: { + sock->family = AF_INET6; + sock->type = CAPWAP_SOCKET_UDPLITE; + sock->socket[CAPWAP_SOCKET_UDP] = net->sock_ctrl[i - 1]; + sock->socket[CAPWAP_SOCKET_UDPLITE] = net->sock_ctrl[i]; + break; + } + } + + break; + } else if (net->sock_data[i] == fd) { + sock->isctrlsocket = 0; + switch (i) { + case CAPWAP_SOCKET_IPV4_UDP: { + sock->family = AF_INET; + sock->type = CAPWAP_SOCKET_UDP; + sock->socket[CAPWAP_SOCKET_UDP] = net->sock_data[i]; + sock->socket[CAPWAP_SOCKET_UDPLITE] = net->sock_data[i + 1]; + break; + } + + case CAPWAP_SOCKET_IPV4_UDPLITE: { + sock->family = AF_INET; + sock->type = CAPWAP_SOCKET_UDPLITE; + sock->socket[CAPWAP_SOCKET_UDP] = net->sock_data[i - 1]; + sock->socket[CAPWAP_SOCKET_UDPLITE] = net->sock_data[i]; + break; + } + + case CAPWAP_SOCKET_IPV6_UDP: { + sock->family = AF_INET6; + sock->type = CAPWAP_SOCKET_UDP; + sock->socket[CAPWAP_SOCKET_UDP] = net->sock_data[i]; + sock->socket[CAPWAP_SOCKET_UDPLITE] = net->sock_data[i + 1]; + break; + } + + case CAPWAP_SOCKET_IPV6_UDPLITE: { + sock->family = AF_INET6; + sock->type = CAPWAP_SOCKET_UDPLITE; + sock->socket[CAPWAP_SOCKET_UDP] = net->sock_data[i - 1]; + sock->socket[CAPWAP_SOCKET_UDPLITE] = net->sock_data[i]; + break; + } + } + + break; + } + } +} + +/* */ +int capwap_sendto(int sock, void* buffer, int size, struct sockaddr_storage* sendfromaddr, struct sockaddr_storage* sendtoaddr) { + int result; + + ASSERT(sock >= 0); + ASSERT(buffer != NULL); + ASSERT(size > 0); + ASSERT(sendtoaddr != NULL); + + /* Information socket */ + if (!sendfromaddr) { + do { + result = sendto(sock, buffer, size, 0, (struct sockaddr*)sendtoaddr, sizeof(struct sockaddr_storage)); + } while ((result < 0) && ((errno == EAGAIN) || (errno == EINTR))); + } else { + struct msghdr msgh; + struct cmsghdr* cmsg; + struct iovec iov; + char cbuf[256]; + + /* */ + memset(&msgh, 0, sizeof(struct msghdr)); + iov.iov_base = buffer; + iov.iov_len = size; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_name = sendtoaddr; + msgh.msg_namelen = sizeof(struct sockaddr_storage); + + if (sendfromaddr->ss_family == AF_INET) { + struct sockaddr_in* addr = (struct sockaddr_in*)sendfromaddr; + +#ifdef IP_PKTINFO + struct in_pktinfo* pkt; + + msgh.msg_control = cbuf; + msgh.msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); + + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = SOL_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + + pkt = (struct in_pktinfo*)CMSG_DATA(cmsg); + memset(pkt, 0, sizeof(struct in_pktinfo)); + memcpy(&pkt->ipi_spec_dst, &addr->sin_addr, sizeof(struct in_addr)); +#elif defined IP_RECVDSTADDR + struct in_addr* in; + + msgh.msg_control = cbuf; + msgh.msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); + + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); + + in = (struct in_addr*)CMSG_DATA(cmsg); + memcpy(in, &addr->sin_addr, sizeof(struct in_addr)); +#else + #error "No method of getting the destination ip address supported" +#endif + } else if (sendfromaddr->ss_family == AF_INET6) { + struct in6_pktinfo* pkt; + struct sockaddr_in6* addr = (struct sockaddr_in6*)sendfromaddr; + + msgh.msg_control = cbuf; + msgh.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); + + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + + pkt = (struct in6_pktinfo*)CMSG_DATA(cmsg); + memset(pkt, 0, sizeof(struct in6_pktinfo)); + memcpy(&pkt->ipi6_addr, &addr->sin6_addr, sizeof(struct in6_addr)); + } + + do { + result = sendmsg(sock, &msgh, 0); + } while ((result < 0) && ((errno == EAGAIN) || (errno == EINTR))); + } + + return ((result > 0) ? size : 0); +} + +/* */ +int capwap_ipv4_mapped_ipv6(struct sockaddr_storage* source, struct sockaddr_storage* dest) { + ASSERT(source != NULL); + ASSERT(dest != NULL); + + memset(dest, 0, sizeof(struct sockaddr_storage)); + + if (source->ss_family == AF_INET) { + struct sockaddr_in* addripv4 = (struct sockaddr_in*)source; + struct sockaddr_in6* addripv6 = (struct sockaddr_in6*)dest; + + addripv6->sin6_family = AF_INET6; + ((unsigned long*)&addripv6->sin6_addr)[2] = htonl(0xffff); + memcpy(&((unsigned long*)&addripv6->sin6_addr)[3], &addripv4->sin_addr, sizeof(unsigned long)); + addripv6->sin6_port = addripv4->sin_port; + + return 1; + } else if (source->ss_family == AF_INET6) { + struct sockaddr_in6* addripv6 = (struct sockaddr_in6*)source; + struct sockaddr_in* addripv4 = (struct sockaddr_in*)dest; + + if (IN6_IS_ADDR_V4MAPPED(&addripv6->sin6_addr)) { + addripv4->sin_family = AF_INET; + memcpy(&addripv4->sin_addr, &((unsigned long*)&addripv6->sin6_addr)[3], sizeof(unsigned long)); + addripv4->sin_port = addripv6->sin6_port; + + return 1; + } + } + + return 0; +} + +/* Convert string into address */ +int capwap_address_from_string(const char* ip, struct sockaddr_storage* address) { + struct addrinfo hints; + struct addrinfo* info = NULL; + char* service = NULL; + char buffer[256]; + char* pos; + int length; + + ASSERT(ip != NULL); + ASSERT(address != NULL); + + /* Init */ + memset(address, 0, sizeof(struct sockaddr_storage)); + length = strlen(ip); + if ((length == 0) || (length >= sizeof(buffer))) { + return 0; + } + + /* */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = 0; + + /* Parsing ipv6 address */ + strcpy(buffer, ip); + pos = &buffer[0]; + if (*pos == '[') { + char* temp = pos + 1; + + pos = temp; + hints.ai_family = AF_INET6; + hints.ai_flags |= AI_NUMERICHOST; + + temp = strchr(temp, ']'); + if (!temp) { + return 0; + } + + *temp = 0; + if (*(temp + 1) == ':') { + service = temp + 2; + hints.ai_flags |= AI_NUMERICSERV; + } else if (*(temp + 1)) { + return 0; + } + } else { + char* temp = strchr(pos, ':'); + if (temp) { + *temp = 0; + service = temp + 1; + hints.ai_flags |= AI_NUMERICSERV; + } + } + + /* Parsing address */ + if (getaddrinfo(pos, service, &hints, &info)) { + return 0; + } + + /* Copy address */ + memcpy(address, info->ai_addr, info->ai_addrlen); + freeaddrinfo(info); + + return 1; +} + +/* Get macaddress from interface */ +int capwap_get_macaddress_from_interface(const char* interface, char* macaddress) { + int sock; + struct ifreq ifr; + int result = 0; + + ASSERT(interface != NULL); + ASSERT(macaddress != NULL); + + sock = socket(PF_PACKET, SOCK_RAW, 0); + if (sock < 0) { + return 0; + } + + memset(&ifr, 0, sizeof(struct ifreq)); + strcpy(ifr.ifr_name, interface); + + if (!ioctl(sock, SIOCGIFHWADDR, &ifr)) { + result = ((ifr.ifr_hwaddr.sa_family == ARPHRD_EUI64) ? 8 : 6); + memcpy(macaddress, ifr.ifr_hwaddr.sa_data, result); + } + + close(sock); + return result; +} + +/* */ +static void capwap_get_network_address(struct sockaddr_storage* addr, struct sockaddr_storage* network, unsigned long bitsmask) { + unsigned long i; + + ASSERT(addr != NULL); + ASSERT(network != NULL); + + memcpy(network, addr, sizeof(struct sockaddr_storage)); + + if (addr->ss_family == AF_INET) { + unsigned long mask = 0xffffffff; + struct sockaddr_in* ipv4addr = (struct sockaddr_in*)network; + + for (i = bitsmask; i < 32; i++) { + mask <<= 1; + } + + ipv4addr->sin_addr.s_addr &= htonl(mask); + } else { + unsigned long pos = bitsmask / 8; + unsigned long delta = bitsmask % 8; + struct sockaddr_in6* ipv6addr = (struct sockaddr_in6*)network; + + if (!delta) { + pos -= 1; /* Optimize for all bits of pos equal 0 */ + } else { + unsigned char mask = 0xff; + + for (i = delta; i < 8; i++) { + mask <<= 1; + } + + ipv6addr->sin6_addr.s6_addr[pos] &= mask; + } + + for (i = pos + 1; i < 16; i++) { + ipv6addr->sin6_addr.s6_addr[i] = 0; + } + } +} + +/* */ +static int capwap_equal_address(struct sockaddr_storage* addr1, struct sockaddr_storage* addr2) { + ASSERT(addr1 != NULL); + ASSERT(addr2 != NULL); + + if (addr1->ss_family == addr2->ss_family) { + if (addr1->ss_family == AF_INET) { + if (((struct sockaddr_in*)addr1)->sin_addr.s_addr == ((struct sockaddr_in*)addr2)->sin_addr.s_addr) { + return 1; + } + } else if (addr1->ss_family == AF_INET6) { + int i; + struct in6_addr* ipv6addr1 = &((struct sockaddr_in6*)addr1)->sin6_addr; + struct in6_addr* ipv6addr2 = &((struct sockaddr_in6*)addr2)->sin6_addr; + + for (i = 0; i < 16; i++) { + if (ipv6addr1->s6_addr[i] != ipv6addr2->s6_addr[i]) { + return 0; + } + } + + return 1; + } + } + + return 0; +} + +/* */ +static int capwap_get_routeaddress(struct sockaddr_storage* local, struct sockaddr_storage* remote, char* oif, int ipv6dualstack, unsigned char table) { + int result = 0; + int end = 0; + + int foundgateway = 0; + unsigned char gatewaytable = 0; + unsigned long gatewaymetric = 0; + struct sockaddr_storage gateway; + + int nlsock; + struct sockaddr_nl nllocal; + socklen_t nllocaladdrlen; + int sndbuf = 32768; + int rcvbuf = 32768; + + struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + + ASSERT(local != NULL); + ASSERT(remote != NULL); + + /* Open netlink route socket */ + nlsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (nlsock < 0) { + capwap_logging_debug("Cannot open netlink socket"); + return 0; + } + + /* Configure socket */ + if (setsockopt(nlsock, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(int)) < 0) { + capwap_logging_debug("Cannot set SO_SNDBUF"); + close(nlsock); + return 0; + } + + if (setsockopt(nlsock, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) < 0) { + capwap_logging_debug("Cannot set SO_RCVBUF"); + close(nlsock); + return 0; + } + + /* Bind */ + memset(&nllocal, 0, sizeof(struct sockaddr_nl)); + nllocal.nl_family = AF_NETLINK; + if (bind(nlsock, (struct sockaddr*)&nllocal, sizeof(struct sockaddr_nl)) < 0) { + capwap_logging_debug("Cannot bind netlink socket"); + close(nlsock); + return 0; + } + + /* Check bind */ + nllocaladdrlen = sizeof(struct sockaddr_nl); + if (getsockname(nlsock, (struct sockaddr*)&nllocal, &nllocaladdrlen) < 0) { + capwap_logging_debug("Cannot getsockname"); + close(nlsock); + return 0; + } + + if ((nllocaladdrlen != sizeof(struct sockaddr_nl)) || (nllocal.nl_family != AF_NETLINK)) { + capwap_logging_debug("Wrong bind netlink socket"); + close(nlsock); + return 0; + } + + /* Send request */ + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = sizeof(req); + req.nlh.nlmsg_type = RTM_GETROUTE; + req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + req.nlh.nlmsg_pid = 0; + req.nlh.nlmsg_seq = 0; + req.g.rtgen_family = AF_UNSPEC; + + if (send(nlsock, (void*)&req, sizeof(req), 0) == sizeof(req)) { + struct sockaddr_nl nladdr; + struct iovec iov; + char buf[16384]; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + iov.iov_base = buf; + while (!result && !end) { + int status; + struct nlmsghdr *h; + + /* Receive response */ + iov.iov_len = sizeof(buf); + status = recvmsg(nlsock, &msg, 0); + if (status < 0) { + if ((errno == EINTR) || (errno == EAGAIN)) + continue; + + capwap_logging_debug("Error from netlink socket: %d", errno); + break; + } else if (status == 0) { + capwap_logging_debug("Receive EOF by netlink socket"); + break; + } + + /* Parsing message */ + h = (struct nlmsghdr*)buf; + while (NLMSG_OK(h, status)) { + if ((h->nlmsg_pid == nllocal.nl_pid) && (h->nlmsg_seq == 0)) { + if ((h->nlmsg_type == NLMSG_DONE) || (h->nlmsg_type == NLMSG_ERROR)) { + end = 1; + break; + } else if (h->nlmsg_type == RTM_NEWROUTE) { + struct rtmsg* r = NLMSG_DATA(h); + int len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg)); + + /* Accept only address IPv4 or IPv6 from main table route */ + if ((len >= 0) && (!table || (r->rtm_table == table)) && (remote->ss_family == r->rtm_family)) { + struct rtattr* tb[RTA_MAX + 1]; + struct rtattr* rta = RTM_RTA(r); + int addrsize = ((r->rtm_family == AF_INET) ? sizeof(struct in_addr) : sizeof(struct in6_addr)); + int defaultgateway = 0; + struct sockaddr_storage dest; + int destmask = r->rtm_dst_len; + char ifname[IFNAMSIZ + 1]; + + /* Parsing rtattr */ + memset(tb, 0, sizeof(struct rtattr*) * (RTA_MAX + 1)); + while (RTA_OK(rta, len)) { + if (rta->rta_type <= RTA_MAX) { + tb[rta->rta_type] = rta; + } + + rta = RTA_NEXT(rta, len); + } + + /* Get device name */ + if (tb[RTA_OIF]) { + if (!if_indextoname(*(int*)RTA_DATA(tb[RTA_OIF]), ifname)) { + ifname[0] = 0; + } + } else { + ifname[0] = 0; + } + + if (!oif || !strcmp(ifname, oif)) { + /* Destination network */ + memset(&dest, 0, sizeof(struct sockaddr_storage)); + dest.ss_family = r->rtm_family; + + if (tb[RTA_DST]) { + void* buffer = ((r->rtm_family == AF_INET) ? (void*)&((struct sockaddr_in*)&dest)->sin_addr : (void*)&((struct sockaddr_in6*)&dest)->sin6_addr); + + memcpy(buffer, RTA_DATA(tb[RTA_DST]), addrsize); + } else if (!r->rtm_dst_len) { + defaultgateway = 1; + } + + /* Check network */ + if (defaultgateway) { + if (tb[RTA_GATEWAY]) { + int update = 0; + unsigned long metric = (tb[RTA_PRIORITY] ? *(unsigned long*)RTA_DATA(tb[RTA_PRIORITY]) : 0); + + /* Detect primary route */ + if (gatewaytable < r->rtm_table) { + update = 1; + } else if ((gatewaytable == r->rtm_table) && (gatewaymetric > metric)) { + update = 1; + } + + if (update) { + void* buffer = (void*)((r->rtm_family == AF_INET) ? (void*)&(((struct sockaddr_in*)&gateway)->sin_addr) : (void*)&(((struct sockaddr_in6*)&gateway)->sin6_addr)); + + foundgateway = 1; + gatewaytable = r->rtm_table; + gatewaymetric = metric; + + memset(&gateway, 0, sizeof(struct sockaddr_storage)); + gateway.ss_family = r->rtm_family; + memcpy(buffer, RTA_DATA(tb[RTA_GATEWAY]), addrsize); + } + } + } else if (tb[RTA_PREFSRC]) { + struct sockaddr_storage remotenetwork; + struct sockaddr_storage destnework; + + capwap_get_network_address(remote, &remotenetwork, destmask); + capwap_get_network_address(&dest, &destnework, destmask); + + if (capwap_equal_address(&remotenetwork, &destnework)) { + void* buffer = (void*)((r->rtm_family == AF_INET) ? (void*)&(((struct sockaddr_in*)local)->sin_addr) : (void*)&(((struct sockaddr_in6*)local)->sin6_addr)); + + result = CAPWAP_ROUTE_LOCAL_ADDRESS; + memset(local, 0, sizeof(struct sockaddr_storage)); + local->ss_family = r->rtm_family; + memcpy(buffer, RTA_DATA(tb[RTA_PREFSRC]), addrsize); + + break; + } + } + } + } + } + } + + h = NLMSG_NEXT(h, status); + } + } + } + + /* */ + if (!result && foundgateway) { + result = CAPWAP_ROUTE_VIA_ADDRESS; + memcpy(local, &gateway, sizeof(struct sockaddr_storage)); + } + + /* */ + close(nlsock); + return ((result > 0) ? result : 0); +} + +/* Get interface flags */ +static short capwap_get_interface_flags(char* iface) { + int sock; + struct ifreq req; + + ASSERT(iface != NULL); + ASSERT(iface[0] != 0); + + sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (sock < 0) { + return 0; + } + + strcpy(req.ifr_name, iface); + if (ioctl(sock, SIOCGIFFLAGS, &req) < 0) { + req.ifr_flags = 0; + } + + close(sock); + + return req.ifr_flags; +} + +/* Return local address from remote address */ +int capwap_get_localaddress_by_remoteaddress(struct sockaddr_storage* local, struct sockaddr_storage* remote, char* oif, int ipv6dualstack) { + int result; + struct sockaddr_storage remotenorm; + + ASSERT(local != NULL); + ASSERT(remote != NULL); + + /* Check output interface */ + if (oif && !strlen(oif)) { + oif = NULL; + } + + /* Loopback address */ + if (remote->ss_family == AF_INET) { + if (((struct sockaddr_in*)remote)->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) { + if (!oif || ((capwap_get_interface_flags(oif) & IFF_LOOPBACK) == IFF_LOOPBACK)) { + memset(local, 0, sizeof(struct sockaddr_storage)); + + local->ss_family = AF_INET; + ((struct sockaddr_in*)local)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + return 1; + } else { + return 0; + } + } + } else if (remote->ss_family == AF_INET6) { + if (!memcmp(&((struct sockaddr_in6*)remote)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr))) { + if (!oif || ((capwap_get_interface_flags(oif) & IFF_LOOPBACK) == IFF_LOOPBACK)) { + memset(local, 0, sizeof(struct sockaddr_storage)); + + local->ss_family = AF_INET6; + memcpy(&((struct sockaddr_in6*)local)->sin6_addr, &in6addr_loopback, sizeof(struct in6_addr)); + + return 1; + } else { + return 0; + } + } + } + + /* Normalize ip address if a ipv4 mapped */ + if (ipv6dualstack && (remote->ss_family == AF_INET6)) { + if (capwap_ipv4_mapped_ipv6(remote, &remotenorm)) { + remote = &remotenorm; + } + } + + /* Get address */ + result = capwap_get_routeaddress(local, remote, oif, ipv6dualstack, RT_TABLE_MAIN); + if (result == CAPWAP_ROUTE_NOT_FOUND) { + return 0; + } else if (result == CAPWAP_ROUTE_VIA_ADDRESS) { + struct sockaddr_storage temp; + + result = capwap_get_routeaddress(&temp, local, oif, ipv6dualstack, RT_TABLE_MAIN); + if (result == CAPWAP_ROUTE_NOT_FOUND) { + return 0; + } + + ASSERT(result == CAPWAP_ROUTE_LOCAL_ADDRESS); + memcpy(local, &temp, sizeof(struct sockaddr_storage)); + } + + return 1; +} + +/* Retrieve interface list */ +void capwap_interface_list(struct capwap_network* net, struct capwap_list* list) { + struct ifaddrs* ifaddrlist; + struct ifaddrs* ifcurrentpos; + + ASSERT(net != NULL); + ASSERT(list != NULL); + + /* Get interface list */ + if (getifaddrs(&ifaddrlist) != 0) + return; + + /* */ + for (ifcurrentpos = ifaddrlist; ifcurrentpos != NULL; ifcurrentpos = ifcurrentpos->ifa_next) { + struct capwap_list_item* item; + struct sockaddr_storage* addr; + + /* No loopback interface */ + if ((ifcurrentpos->ifa_flags & IFF_LOOPBACK) != 0) { + continue; + } + + /* Only IPv4 and IPv6 */ + if ((ifcurrentpos->ifa_addr == NULL) || ((ifcurrentpos->ifa_addr->sa_family != AF_INET) && (ifcurrentpos->ifa_addr->sa_family != AF_INET6))) { + continue; + } + + /* Filter family */ + if ((net->sock_family != AF_UNSPEC) && (net->sock_family != ifcurrentpos->ifa_addr->sa_family)) { + continue; + } + + /* Filter interface */ + if ((net->bind_interface[0] != 0) && (strcmp(net->bind_interface, ifcurrentpos->ifa_name) != 0)) { + continue; + } + + /* Add local address */ + item = capwap_itemlist_create(sizeof(struct sockaddr_storage)); + addr = (struct sockaddr_storage*)item->item; + + memset(addr, 0, sizeof(struct sockaddr_storage)); + addr->ss_family = ifcurrentpos->ifa_addr->sa_family; + CAPWAP_SET_NETWORK_PORT(addr, net->bind_sock_ctrl_port); + + if (addr->ss_family == AF_INET) { + memcpy(&((struct sockaddr_in*)addr)->sin_addr, &((struct sockaddr_in*)ifcurrentpos->ifa_addr)->sin_addr, sizeof(struct in_addr)); + } else if (addr->ss_family == AF_INET6) { + memcpy(&((struct sockaddr_in6*)addr)->sin6_addr, &((struct sockaddr_in6*)ifcurrentpos->ifa_addr)->sin6_addr, sizeof(struct in6_addr)); + } + + /* Add address */ + capwap_itemlist_insert_after(list, NULL, item); + } + + /* Free */ + freeifaddrs(ifaddrlist); +} diff --git a/src/common/capwap_network.h b/src/common/capwap_network.h new file mode 100644 index 0000000..01684f2 --- /dev/null +++ b/src/common/capwap_network.h @@ -0,0 +1,87 @@ +#ifndef __CAPWAP_NETWORK_HEADER__ +#define __CAPWAP_NETWORK_HEADER__ + +#include "capwap_array.h" +#include "capwap_list.h" + +/* Standard Configuration */ +#define CAPWAP_CONTROL_PORT 5246 +#define CAPWAP_MAX_PACKET_SIZE 65535 + +#define CAPWAP_MACADDRESS_NONE 0 +#define CAPWAP_MACADDRESS_EUI48 6 +#define CAPWAP_MACADDRESS_EUI64 8 +#define CAPWAP_MACADDRESS_MAX_SIZE CAPWAP_MACADDRESS_EUI64 + +/* Helper */ +#define CAPWAP_GET_NETWORK_PORT(address) ntohs((((address)->ss_family == AF_INET) ? ((struct sockaddr_in*)(address))->sin_port : ((struct sockaddr_in6*)(address))->sin6_port)) +#define CAPWAP_SET_NETWORK_PORT(address, port) if ((address)->ss_family == AF_INET) { \ + ((struct sockaddr_in*)(address))->sin_port = htons(port); \ + } else if ((address)->ss_family == AF_INET6) { \ + ((struct sockaddr_in6*)(address))->sin6_port = htons(port); \ + } + +/* */ +#define CAPWAP_MAX_SOCKETS 4 +#define CAPWAP_SOCKET_IPV4_UDP 0 +#define CAPWAP_SOCKET_IPV4_UDPLITE 1 +#define CAPWAP_SOCKET_IPV6_UDP 2 +#define CAPWAP_SOCKET_IPV6_UDPLITE 3 + +/* */ +#define CAPWAP_RECV_ERROR_SOCKET -1 +#define CAPWAP_RECV_ERROR_TIMEOUT -2 +#define CAPWAP_RECV_ERROR_INTR -3 + +/* Socket Flags */ +#define CAPWAP_IPV6ONLY_FLAG 0x00000001 + +/* Network struct */ +struct capwap_network { + int sock_family; /* Address family used by the server. */ + unsigned short bind_sock_ctrl_port; /* Port number to listen control protocol. */ + char bind_interface[IFNAMSIZ]; + + int sock_ctrl[CAPWAP_MAX_SOCKETS]; + int bind_ctrl_flags; + + int sock_data[CAPWAP_MAX_SOCKETS]; + int bind_data_flags; +}; + +#define CAPWAP_SOCKET_UDP 0 +#define CAPWAP_SOCKET_UDPLITE 1 + +/* Network socket */ +struct capwap_socket { + int type; + int family; + int socket[2]; + int isctrlsocket; +}; + +void capwap_network_init(struct capwap_network* net); +int capwap_network_set_pollfd(struct capwap_network* net, struct pollfd* fds, int fdscount); +void capwap_interface_list(struct capwap_network* net, struct capwap_list* list); + +int capwap_get_macaddress_from_interface(const char* interface, char* macaddress); + +#define CAPWAP_DATA_SOCKET 0 +#define CAPWAP_CTRL_SOCKET 1 +int capwap_get_socket(struct capwap_network* net, int socketfamily, int socketprotocol, int isctrlsocket); +void capwap_get_network_socket(struct capwap_network* net, struct capwap_socket* sock, int fd); + +int capwap_bind_sockets(struct capwap_network* net); +void capwap_close_sockets(struct capwap_network* net); + +int capwap_compare_ip(struct sockaddr_storage* addr1, struct sockaddr_storage* addr2); + +int capwap_sendto(int sock, void* buffer, int size, struct sockaddr_storage* sendfromaddr, struct sockaddr_storage* sendtoaddr); +int capwap_recvfrom(struct pollfd* fds, int fdscount, void* buffer, int* size, struct sockaddr_storage* recvfromaddr, struct sockaddr_storage* recvtoaddr, struct timeout_control* timeout); + +int capwap_ipv4_mapped_ipv6(struct sockaddr_storage* source, struct sockaddr_storage* dest); +int capwap_address_from_string(const char* ip, struct sockaddr_storage* address); + +int capwap_get_localaddress_by_remoteaddress(struct sockaddr_storage* local, struct sockaddr_storage* remote, char* oif, int ipv6dualstack); + +#endif /* __CAPWAP_NETWORK_HEADER__ */ diff --git a/src/common/capwap_protocol.c b/src/common/capwap_protocol.c new file mode 100644 index 0000000..e50aa64 --- /dev/null +++ b/src/common/capwap_protocol.c @@ -0,0 +1,1327 @@ +#include "capwap.h" +#include "capwap_protocol.h" +#include "capwap_network.h" +#include "capwap_dfa.h" +#include "capwap_list.h" +#include "capwap_array.h" +#include "md5.h" + +/* */ +static struct capwap_fragment_sender* capwap_defragment_add_sender(capwap_fragment_list* defraglist, struct sockaddr_storage* sendaddr, struct capwap_header* header); +static struct capwap_list_item* capwap_defragment_create_packet(void* payload, int size, unsigned short offset); + +/* Check valid packet */ +int capwap_sanity_check(int isctrlsocket, int state, void* buffer, int buffersize, int dtlsctrlenable, int dtlsdataenable) { + struct capwap_preamble* preamble; + + ASSERT(buffer != NULL); + ASSERT(buffersize > sizeof(struct capwap_preamble)); + + preamble = (struct capwap_preamble*)buffer; + if (preamble->version != CAPWAP_PROTOCOL_VERSION) { + return CAPWAP_WRONG_PACKET; + } + + if (isctrlsocket) { + if (dtlsctrlenable) { + if ((preamble->type == CAPWAP_PREAMBLE_DTLS_HEADER) && (buffersize >= sizeof(struct capwap_dtls_header))) { + if (state == CAPWAP_DISCOVERY_STATE) { + return CAPWAP_WRONG_PACKET; + } + + return CAPWAP_DTLS_PACKET; + } else if ((preamble->type == CAPWAP_PREAMBLE_HEADER) && (buffersize >= sizeof(struct capwap_header))) { + struct capwap_header* header = (struct capwap_header*)preamble; + if (buffersize >= GET_HLEN_HEADER(header) * 4) { + if ((state != CAPWAP_DISCOVERY_STATE) && (state != CAPWAP_UNDEF_STATE)) { + return CAPWAP_WRONG_PACKET; + } + + return CAPWAP_PLAIN_PACKET; + } + } + } else { + if ((preamble->type == CAPWAP_PREAMBLE_HEADER) && (buffersize >= sizeof(struct capwap_header))) { + struct capwap_header* header = (struct capwap_header*)preamble; + if (buffersize >= GET_HLEN_HEADER(header) * 4) { + return CAPWAP_PLAIN_PACKET; + } + } + } + } else { + if ((state != CAPWAP_DATA_CHECK_TO_RUN_STATE) && (state != CAPWAP_RUN_STATE) && (state != CAPWAP_UNDEF_STATE)) { + return CAPWAP_WRONG_PACKET; + } + + if (dtlsdataenable) { + if ((preamble->type == CAPWAP_PREAMBLE_DTLS_HEADER) && (buffersize >= sizeof(struct capwap_dtls_header))) { + return CAPWAP_DTLS_PACKET; + } + } else { + if ((preamble->type == CAPWAP_PREAMBLE_HEADER) && (buffersize >= sizeof(struct capwap_header))) { + struct capwap_header* header = (struct capwap_header*)preamble; + if (buffersize >= GET_HLEN_HEADER(header) * 4) { + return CAPWAP_PLAIN_PACKET; + } + } + } + } + + return CAPWAP_WRONG_PACKET; +} + +/* */ +int capwap_defragment_packets(struct sockaddr_storage* sendaddr, void* buffer, int buffersize, capwap_fragment_list* defraglist, struct capwap_packet* packet) { + struct capwap_header* header = (struct capwap_header*)buffer; + struct capwap_fragment_sender* defragsend; + int headersize; + + ASSERT(sendaddr != NULL); + ASSERT(buffer != NULL); + ASSERT(buffersize > sizeof(struct capwap_header)); + ASSERT(defraglist != NULL); + ASSERT(packet != NULL); + + headersize = GET_HLEN_HEADER(header) * 4; + defragsend = capwap_defragment_find_sender(defraglist, sendaddr); + + if (IS_FLAG_F_HEADER(header)) { + struct capwap_list_item* searchpacket; + struct capwap_fragment_packet* itempacket; + unsigned short fragid = GET_FRAGMENT_ID_HEADER(header); + unsigned short fragoffset = GET_FRAGMENT_OFFSET_HEADER(header); + char* payload = (char*)buffer + headersize; + int payloadsize = buffersize - headersize; + + /* Size of payload is multiple of 64bits */ + if ((payloadsize % 8) != 0) { + return CAPWAP_WRONG_FRAGMENT; + } + + if (!defragsend) { + /* Create new defragment item */ + defragsend = capwap_defragment_add_sender(defraglist, sendaddr, header); + if (!defragsend) { + return CAPWAP_WRONG_FRAGMENT; + } + } else if (fragid != defragsend->fragment_id) { + /* Wrong fragment id */ + capwap_defragment_remove_sender(defraglist, sendaddr); + return CAPWAP_WRONG_FRAGMENT; + } + + if (fragoffset == 0) { + /* Save header of the first packet of fragmentation */ + defragsend->header = (struct capwap_header*)capwap_clone(header, headersize); + } + + /* Defragment payload */ + searchpacket = defragsend->packetlist->last; + if (!searchpacket) { + struct capwap_list_item* packet = capwap_defragment_create_packet(payload, payloadsize, fragoffset); + capwap_itemlist_insert_before(defragsend->packetlist, NULL, packet); + } else { + while (searchpacket != NULL) { + itempacket = (struct capwap_fragment_packet*)searchpacket->item; + ASSERT(itempacket != NULL); + + if (itempacket->offset > fragoffset) { + if (!searchpacket->prev) { + struct capwap_list_item* packet = capwap_defragment_create_packet(payload, payloadsize, fragoffset); + capwap_itemlist_insert_before(defragsend->packetlist, searchpacket, packet); + break; + } + } else if (itempacket->offset < fragoffset) { + if (!defragsend->islastrecv || (searchpacket != defragsend->packetlist->last)) { + struct capwap_list_item* packet = capwap_defragment_create_packet(payload, payloadsize, fragoffset); + capwap_itemlist_insert_after(defragsend->packetlist, searchpacket, packet); + } + + break; + } else { + /* Duplicate packet */ + break; + } + + /* Prev fragment */ + searchpacket = searchpacket->prev; + } + } + + /* If last packet, mark end of fragmentation */ + if (!defragsend->islastrecv) { + defragsend->islastrecv = IS_FLAG_L_HEADER(header); + } + + /* Check if defragmentation is completed */ + if (defragsend->islastrecv) { + unsigned long checkoffset = 0; + + payloadsize = 0; + searchpacket = defragsend->packetlist->first; + while (searchpacket != NULL) { + itempacket = (struct capwap_fragment_packet*)searchpacket->item; + if (checkoffset != itempacket->offset) { + return CAPWAP_REQUEST_MORE_FRAGMENT; + } + + /* Next fragment */ + payloadsize += itempacket->size; + checkoffset += itempacket->size / 8; + searchpacket = searchpacket->next; + } + + /* Defragment complete */ + ASSERT(defragsend->header != NULL); + headersize = GET_HLEN_HEADER(defragsend->header) * 4; + packet->packetsize = headersize + payloadsize; + packet->header = (struct capwap_header*)capwap_alloc(packet->packetsize); + if (!packet->header) { + capwap_outofmemory(); + } + + /* Copy header and remove fragmention information */ + memcpy(packet->header, defragsend->header, headersize); + SET_FLAG_F_HEADER(packet->header, 0); + SET_FLAG_L_HEADER(packet->header, 0); + SET_FRAGMENT_ID_HEADER(packet->header, 0); + SET_FRAGMENT_OFFSET_HEADER(packet->header, 0); + + /* Copy payload */ + packet->payload = (char*)packet->header + headersize; + payload = packet->payload; + + searchpacket = defragsend->packetlist->first; + while (searchpacket != NULL) { + itempacket = (struct capwap_fragment_packet*)searchpacket->item; + memcpy(payload, itempacket->buffer, itempacket->size); + payload += itempacket->size; + + /* Next */ + searchpacket = searchpacket->next; + } + + capwap_defragment_remove_sender(defraglist, sendaddr); + return CAPWAP_RECEIVE_COMPLETE_PACKET; + } + + return CAPWAP_REQUEST_MORE_FRAGMENT; + } else { + /* Check if already received fragment packets */ + if (defragsend) { + /* Overlap fragment packet with complete packet */ + capwap_defragment_remove_sender(defraglist, sendaddr); + } else { + /* Copy buffer */ + packet->packetsize = buffersize; + packet->header = (struct capwap_header*)capwap_clone(buffer, packet->packetsize); + packet->payload = (void*)((char*)buffer + headersize); + + return CAPWAP_RECEIVE_COMPLETE_PACKET; + } + } + + return CAPWAP_WRONG_FRAGMENT; +} + +/* */ +capwap_fragment_list* capwap_defragment_init_list(void) { + return capwap_list_create(); +} + +/* */ +static void capwap_defragment_free_packetlist(struct capwap_list* packetlist) { + struct capwap_list_item* search; + + ASSERT(packetlist != NULL); + + search = packetlist->first; + while (search) { + struct capwap_fragment_packet* packet = (struct capwap_fragment_packet*)search->item; + ASSERT(packet->buffer != NULL); + + capwap_free(packet->buffer); + + /* Next */ + search = search->next; + } + + capwap_list_free(packetlist); +} + +/* */ +void capwap_defragment_flush_list(capwap_fragment_list* defraglist) { + struct capwap_fragment_sender* item; + + ASSERT(defraglist != NULL); + + while (defraglist->first) { + item = (struct capwap_fragment_sender*)defraglist->first->item; + ASSERT(item != NULL); + ASSERT(item->packetlist != NULL); + + capwap_defragment_free_packetlist(item->packetlist); + capwap_itemlist_free(capwap_itemlist_remove(defraglist, defraglist->first)); + } +} + +/* */ +void capwap_defragment_free_list(capwap_fragment_list* defraglist) { + ASSERT(defraglist != NULL); + + capwap_defragment_flush_list(defraglist); + capwap_list_free(defraglist); +} + +/* */ +struct capwap_fragment_sender* capwap_defragment_find_sender(capwap_fragment_list* defraglist, struct sockaddr_storage* sendaddr) { + struct capwap_fragment_sender* item; + struct capwap_list_item* search; + + ASSERT(defraglist != NULL); + ASSERT(sendaddr != NULL); + + search = defraglist->first; + while (search) { + item = (struct capwap_fragment_sender*)search->item; + ASSERT(item != NULL); + + if (!capwap_compare_ip(sendaddr, &item->sendaddr)) { + return item; + } + + search = search->next; + } + + return NULL; +} + +/* */ +static struct capwap_fragment_sender* capwap_defragment_add_sender(capwap_fragment_list* defraglist, struct sockaddr_storage* sendaddr, struct capwap_header* header) { + struct capwap_list_item* item; + struct capwap_fragment_sender* sender; + int headersize; + + ASSERT(defraglist != NULL); + ASSERT(sendaddr != NULL); + ASSERT(header != NULL); + + item = capwap_itemlist_create(sizeof(struct capwap_fragment_sender)); + sender = (struct capwap_fragment_sender*)item->item; + + memset(sender, 0, sizeof(struct capwap_fragment_sender)); + memcpy(&sender->sendaddr, sendaddr, sizeof(struct sockaddr_storage)); + sender->fragment_id = GET_FRAGMENT_ID_HEADER(header); + + headersize = GET_HLEN_HEADER(header) * 4; + sender->header = (struct capwap_header*)capwap_alloc(headersize); + if (!sender->header) { + capwap_outofmemory(); + } + + memcpy(sender->header, header, headersize); + sender->packetlist = capwap_list_create(); + + /* Add item to list */ + capwap_itemlist_insert_after(defraglist, NULL, item); + return sender; +} + +/* */ +int capwap_defragment_remove_sender(capwap_fragment_list* defraglist, struct sockaddr_storage* sendaddr) { + int found = 0; + struct capwap_fragment_sender* item; + struct capwap_list_item* search; + + ASSERT(defraglist != NULL); + ASSERT(sendaddr != NULL); + + search = defraglist->first; + while (search) { + item = (struct capwap_fragment_sender*)search->item; + ASSERT(item != NULL); + + if (!capwap_compare_ip(sendaddr, &item->sendaddr)) { + ASSERT(item->packetlist); + + found = 1; + capwap_defragment_free_packetlist(item->packetlist); + capwap_itemlist_free(capwap_itemlist_remove(defraglist, search)); + break; + } + + search = search->next; + } + + return found; +} + +/* */ +static struct capwap_list_item* capwap_defragment_create_packet(void* payload, int size, unsigned short offset) { + struct capwap_fragment_packet* packet; + struct capwap_list_item* item; + + ASSERT(payload != NULL); + ASSERT(size > 0); + + /* New fragment */ + item = capwap_itemlist_create(sizeof(struct capwap_fragment_packet)); + packet = (struct capwap_fragment_packet*)item->item; + + memset(packet, 0, sizeof(struct capwap_fragment_packet)); + packet->buffer = capwap_clone(payload, size); + packet->size = size; + packet->offset = offset; + + return item; +} + +/* */ +void capwap_free_packet(struct capwap_packet* packet) { + ASSERT(packet != NULL); + + if (packet->header) { + capwap_free(packet->header); + memset(packet, 0, sizeof(struct capwap_packet)); + } +} + +/* Creare tx packet */ +struct capwap_build_packet* capwap_tx_packet_create(unsigned short radioid, unsigned short binding) { + struct capwap_build_packet* packet; + struct capwap_header* header; + + packet = (struct capwap_build_packet*)capwap_alloc(sizeof(struct capwap_build_packet)); + if (!packet) { + capwap_outofmemory(); + } + + memset(packet, 0, sizeof(struct capwap_build_packet)); + header = &packet->header; + + /* Standard configuration */ + SET_VERSION_HEADER(header, CAPWAP_PROTOCOL_VERSION); + SET_TYPE_HEADER(header, CAPWAP_PREAMBLE_HEADER); + SET_HLEN_HEADER(header, sizeof(struct capwap_header) / 4); + SET_RID_HEADER(header, radioid); + SET_WBID_HEADER(header, binding); + + /* Message elements list */ + packet->elementslist = capwap_list_create(); + + return packet; +} + +/* Destroy tx packet */ +void capwap_build_packet_free(struct capwap_build_packet* buildpacket) { + ASSERT(buildpacket != NULL); + + /* */ + capwap_list_free(buildpacket->elementslist); + capwap_free(buildpacket); +} + +/* Add radio macaddress to packet */ +void capwap_build_packet_set_radio_macaddress(struct capwap_build_packet* buildpacket, int radiotype, char* macaddress) { + struct capwap_header* header; + + ASSERT(buildpacket != NULL); + + header = &buildpacket->header; + if (radiotype == CAPWAP_MACADDRESS_NONE) { + if (IS_FLAG_M_HEADER(header)) { + if (!IS_FLAG_W_HEADER(header)) { + SET_HLEN_HEADER(header, sizeof(struct capwap_header) / 4); + } else { + struct capwap_wireless_information* wireless = GET_WIRELESS_INFORMATION_STRUCT(header); + int lengthpadded = (((sizeof(struct capwap_wireless_information) + wireless->length) + 3) / 4); + + /* Move wireless information */ + memmove(((char*)header + sizeof(struct capwap_header)), wireless, lengthpadded * 4); + SET_HLEN_HEADER(header, (sizeof(struct capwap_header) / 4) + lengthpadded); + } + + SET_FLAG_M_HEADER(header, 0); + } + } else { + int i; + int radiosizepadded; + struct capwap_mac_address* radio; + int size = sizeof(struct capwap_header) / 4; + + ASSERT(macaddress != NULL); + ASSERT((radiotype == CAPWAP_MACADDRESS_EUI48) || (radiotype == CAPWAP_MACADDRESS_EUI64)); + + if (IS_FLAG_M_HEADER(header)) { + radio = GET_RADIO_MAC_ADDRESS_STRUCT(header); + + if (radio->length == radiotype) { + /* Rewrite mac address */ + memcpy(radio->address, macaddress, radiotype); + return; + } + + /* Remove old radio mac address */ + capwap_build_packet_set_radio_macaddress(buildpacket, CAPWAP_MACADDRESS_NONE, NULL); + } + + /* Radio mac address size*/ + radio = (struct capwap_mac_address*)((char*)header + sizeof(struct capwap_header)); + radiosizepadded = (((sizeof(struct capwap_mac_address) + radiotype) + 3) / 4); + size += radiosizepadded; + + /* Wireless information */ + if (IS_FLAG_W_HEADER(header)) { + struct capwap_wireless_information* wireless = GET_WIRELESS_INFORMATION_STRUCT(header); + int lengthpadded = (((sizeof(struct capwap_wireless_information) + wireless->length) + 3) / 4); + + memmove((char*)radio + radiosizepadded, wireless, lengthpadded * 4); + size += lengthpadded; + } + + radio->length = radiotype; + memcpy(radio->address, macaddress, radiotype); + for (i = (radiosizepadded * 4) - 2; i >= radiotype; i--) { + radio->address[i] = 0; + } + + SET_FLAG_M_HEADER(header, 1); + SET_HLEN_HEADER(header, size); + } +} + +/* Add Wireless Specific Information to packet */ +void capwap_build_packet_set_wireless_information(struct capwap_build_packet* buildpacket, void* buffer, unsigned char length) { + struct capwap_header* header; + int size; + + ASSERT(buildpacket != NULL); + + header = &buildpacket->header; + + /* Calculate size of header */ + size = sizeof(struct capwap_header) / 4; + if (IS_FLAG_M_HEADER(header)) { + struct capwap_mac_address* radio = GET_RADIO_MAC_ADDRESS_STRUCT(header); + size += ((sizeof(struct capwap_mac_address) + radio->length) + 3) / 4; + } + + /* Remove old wireless information */ + if (IS_FLAG_W_HEADER(header)) { + SET_HLEN_HEADER(header, size); + } + + /* Add new wireless information */ + if (length > 0) { + int i; + struct capwap_wireless_information* wireless; + int lengthpadded = (((sizeof(struct capwap_wireless_information) + length) + 3) / 4); + + ASSERT(buffer != NULL); + + wireless = GET_WIRELESS_INFORMATION_STRUCT(header); + wireless->length = length; + memcpy(wireless->data, buffer, length); + for (i = (lengthpadded * 4) - 2; i >= length; i--) { + wireless->data[i] = 0; + } + + /* Update size */ + size += lengthpadded; + SET_HLEN_HEADER(header, size); + } +} + +/* Set control message type */ +void capwap_build_packet_set_control_message_type(struct capwap_build_packet* buildpacket, unsigned long type, unsigned char seq) { + ASSERT(buildpacket != NULL); + + buildpacket->ctrlmsg.type = htonl(type); + buildpacket->ctrlmsg.seq = seq; + buildpacket->ctrlmsg.length = 0; + buildpacket->ctrlmsg.flags = 0; +} + +/* Add message element */ +void capwap_build_packet_add_message_element(struct capwap_build_packet* buildpacket, struct capwap_message_element* element) { + struct capwap_list_item* itemlist; + unsigned long length; + + ASSERT(buildpacket != NULL); + + if ((element == NULL) || (element->length == 0)) { + capwap_logging_debug("Warning, add null element to packet"); + return; + } + + /* Create item and add into last position of list*/ + length = ntohs(element->length) + sizeof(struct capwap_message_element); + itemlist = capwap_itemlist_create_with_item(element, length); + capwap_itemlist_insert_after(buildpacket->elementslist, NULL, itemlist); + + /* */ + if (buildpacket->isctrlmsg) { + buildpacket->ctrlmsg.length = htons(ntohs(buildpacket->ctrlmsg.length) + length); + } else { + buildpacket->datamsg.length = htons(ntohs(buildpacket->datamsg.length) + length); + } +} + +/* Generate fragment packets */ +int capwap_fragment_build_packet(struct capwap_build_packet* buildpacket, capwap_fragment_packet_array* packets, unsigned short mtu, unsigned short fragmentid) { + unsigned short i; + unsigned short reqpacket; + unsigned long length = 0; + unsigned short headerlength = 0; + struct capwap_header* header; + unsigned short fragmentposition = 0; + struct capwap_list_item* item = NULL; + unsigned long itempos = 0; + + ASSERT(buildpacket != NULL); + ASSERT(packets != NULL); + + /* Free array */ + capwap_fragment_free(packets); + if ((mtu > 0) && (mtu < CAPWAP_HEADER_MAX_SIZE)) { + /* Mtu must be greater than the maximum size of capwap header */ + capwap_logging_debug("The mtu is too small: %hu", mtu); + return -1; + } + + /* Get length raw packet */ + header = &buildpacket->header; + headerlength = GET_HLEN_HEADER(header) * 4; + if (buildpacket->isctrlmsg) { + length = sizeof(struct capwap_control_message) + ntohs(buildpacket->ctrlmsg.length); + } else if (IS_FLAG_K_HEADER(header)) { + length = sizeof(struct capwap_data_message) + ntohs(buildpacket->datamsg.length); + } + + /* Retrive number of request packet for send a capwap message */ + if (!mtu || ((headerlength + length) <= mtu)) { + reqpacket = 1; + } else { + unsigned long lengthpayload = length; + unsigned short mtupayload; + + /* Detect mtu payload */ + mtupayload = mtu - sizeof(struct capwap_header); + mtupayload -= mtupayload % 8; + + /* Calculate number of request packets without size of header */ + if (IS_FLAG_M_HEADER(header)) { + struct capwap_mac_address* radio = GET_RADIO_MAC_ADDRESS_STRUCT(header); + lengthpayload += ((sizeof(struct capwap_mac_address) + radio->length) + 3) / 4; + } + + if (IS_FLAG_W_HEADER(header)) { + struct capwap_wireless_information* wireless = GET_WIRELESS_INFORMATION_STRUCT(header); + lengthpayload += ((sizeof(struct capwap_wireless_information) + wireless->length) + 3) / 4; + } + + /* Request packet padded */ + reqpacket = (lengthpayload + (mtupayload - 1)) / mtupayload; + } + + /* Create packets */ + capwap_array_resize(packets, reqpacket); + for (i = 0; i < reqpacket; i++) { + long payloadsize = 0; + struct capwap_packet* packet = (struct capwap_packet*)capwap_array_get_item_pointer(packets, i); + memset(packet, 0, sizeof(struct capwap_packet)); + + if (reqpacket == 1) { + /* Build header */ + packet->packetsize = headerlength + length; + packet->header = (struct capwap_header*)capwap_alloc(packet->packetsize); + + memcpy(packet->header, &buildpacket->header, headerlength); + packet->payload = (void*)(((char*)packet->header) + headerlength); + payloadsize = length; + + /* Disable fragmentation */ + SET_FLAG_F_HEADER(packet->header, 0); + SET_FRAGMENT_ID_HEADER(packet->header, 0); + SET_FRAGMENT_OFFSET_HEADER(packet->header, 0); + SET_FLAG_L_HEADER(packet->header, 0); + } else { + unsigned short headerpos = ((i == 0) ? headerlength : sizeof(struct capwap_header)); + unsigned short mtupayload; + + /* Detect mtu payload */ + mtupayload = mtu - headerpos; + mtupayload -= mtupayload % 8; + + /* Build header */ + packet->packetsize = headerpos + mtupayload; + packet->header = (struct capwap_header*)capwap_alloc(packet->packetsize); + memcpy(packet->header, &buildpacket->header, headerpos); + packet->payload = (void*)(((char*)packet->header) + headerpos); + payloadsize = mtupayload; + + if (i > 0) { + /* Radio mac address and wireless information is sent only into first packet */ + SET_FLAG_M_HEADER(packet->header, 0); + SET_FLAG_W_HEADER(packet->header, 0); + SET_HLEN_HEADER(packet->header, sizeof(struct capwap_header) / 4); + } + + /* Use fragmentation */ + SET_FLAG_F_HEADER(packet->header, 1); + SET_FRAGMENT_ID_HEADER(packet->header, fragmentid); + SET_FRAGMENT_OFFSET_HEADER(packet->header, fragmentposition); + SET_FLAG_L_HEADER(packet->header, (((i + 1) == reqpacket) ? 1 : 0)); + } + + /* Build payload */ + if (length > 0) { + char* pos = (char*)packet->payload; + + /* Data/Control Message */ + if (i == 0) { + if (buildpacket->isctrlmsg) { + /* Control Message header can not fragment */ + if (payloadsize < sizeof(struct capwap_control_message)) { + capwap_logging_debug("Unable fragments packet, mtu is too small"); + capwap_fragment_free(packets); + return -1; + } + + memcpy(packet->payload, &buildpacket->ctrlmsg, sizeof(struct capwap_control_message)); + pos += sizeof(struct capwap_control_message); + payloadsize -= sizeof(struct capwap_control_message); + } else if (IS_FLAG_K_HEADER(header)) { + /* Data Message header can not fragment */ + if (payloadsize < sizeof(struct capwap_data_message)) { + capwap_logging_debug("Unable fragments packet, mtu is too small"); + capwap_fragment_free(packets); + return -1; + } + + memcpy(packet->payload, &buildpacket->datamsg, sizeof(struct capwap_data_message)); + pos += sizeof(struct capwap_data_message); + payloadsize -= sizeof(struct capwap_data_message); + } + + /* Configure message elements */ + item = buildpacket->elementslist->first; + itempos = 0; + } + + /* Add message elements */ + while ((item != NULL) && (payloadsize > 0)) { + unsigned short elementcopy; + unsigned short elementlength; + struct capwap_message_element* element = (struct capwap_message_element*)item->item; + + ASSERT(element != NULL); + + /* Copy message element */ + elementlength = sizeof(struct capwap_message_element) + ntohs(element->length); + elementcopy = min(elementlength - itempos, payloadsize); + memcpy(pos, &((char*)element)[itempos], elementcopy); + + pos += elementcopy; + itempos += elementcopy; + payloadsize -= elementcopy; + ASSERT(payloadsize >= 0); + + /* Next element */ + if (itempos == elementlength) { + item = item->next; + itempos = 0; + } + } + + if (((i + 1) == reqpacket) && (payloadsize > 0)) { + packet->packetsize -= payloadsize; + } else { + ASSERT(payloadsize == 0); + } + } + } + + /* Return 1 if fragment packet */ + return ((reqpacket > 1) ? 1 : 0); +} + +/* */ +void capwap_fragment_free(capwap_fragment_packet_array* packets) { + unsigned long i; + + ASSERT(packets != NULL); + + if (packets->count == 0) { + return; + } + + for (i = 0; i < packets->count; i++) { + capwap_free_packet((struct capwap_packet*)capwap_array_get_item_pointer(packets, i)); + } + + capwap_array_resize(packets, 0); +} + +/* */ +struct capwap_build_packet* capwap_rx_packet_create(void* buffer, int buffersize, int isctrlpacket) { + struct capwap_build_packet* buildpacket; + struct capwap_header* header; + char* pos = (char*)buffer; + int length; + int controlsize; + + ASSERT(buffer != NULL); + ASSERT(buffersize > 0); + + /* Header */ + header = (struct capwap_header*)buffer; + length = GET_HLEN_HEADER(header) * 4; + if (buffersize < length) { + return NULL; + } + + /* Build packet */ + buildpacket = (struct capwap_build_packet*)capwap_alloc(sizeof(struct capwap_build_packet)); + if (!buildpacket) { + capwap_outofmemory(); + } + + /* */ + memset(buildpacket, 0, sizeof(struct capwap_build_packet)); + buildpacket->isctrlmsg = (isctrlpacket ? 1 : 0); + + /* Header packet */ + memcpy(&buildpacket->header, pos, length); + pos += length; + buffersize -= length; + + if (buildpacket->isctrlmsg) { + if (buffersize < sizeof(struct capwap_control_message)) { + capwap_logging_debug("Invalid capwap packet, size of control message body is great of raw packet"); + capwap_free(buildpacket); + return NULL; + } + + /* Control message header */ + memcpy(&buildpacket->ctrlmsg, pos, sizeof(struct capwap_control_message)); + pos += sizeof(struct capwap_control_message); + buffersize -= sizeof(struct capwap_control_message); + + /* Check the packet size */ + controlsize = ntohs(buildpacket->ctrlmsg.length); + if (controlsize > buffersize) { + capwap_logging_debug("Invalid capwap packet, size of control message body is great of raw packet"); + capwap_free(buildpacket); + return NULL; + } + + /* Message elements list */ + buildpacket->elementslist = capwap_list_create(); + while (controlsize > 0) { + struct capwap_message_element* element = (struct capwap_message_element*)pos; + int elementsize = ntohs(element->length) + sizeof(struct capwap_message_element); + struct capwap_list_item* itemlist; + + /* Clone message element */ + itemlist = capwap_itemlist_create(elementsize); + memcpy(itemlist->item, pos, elementsize); + capwap_itemlist_insert_after(buildpacket->elementslist, NULL, itemlist); + + /* Next */ + pos += elementsize; + controlsize -= elementsize; + buffersize -= elementsize; + } + } else { + if (IS_FLAG_K_HEADER(&buildpacket->header)) { + if (buffersize < sizeof(struct capwap_data_message)) { + capwap_logging_debug("Invalid capwap packet, size of data message body is great of raw packet"); + capwap_free(buildpacket); + return NULL; + } + + /* Control message header */ + memcpy(&buildpacket->datamsg, pos, sizeof(struct capwap_data_message)); + pos += sizeof(struct capwap_data_message); + buffersize -= sizeof(struct capwap_data_message); + + /* Check the packet size */ + controlsize = ntohs(buildpacket->datamsg.length); + if (controlsize > buffersize) { + capwap_logging_debug("Invalid capwap packet, size of data message body is great of raw packet"); + capwap_free(buildpacket); + return NULL; + } + + /* Message elements list */ + buildpacket->elementslist = capwap_list_create(); + while (controlsize > 0) { + struct capwap_message_element* element = (struct capwap_message_element*)pos; + int elementsize = ntohs(element->length) + sizeof(struct capwap_message_element); + struct capwap_list_item* itemlist; + + /* Clone message element */ + itemlist = capwap_itemlist_create(elementsize); + memcpy(itemlist->item, pos, elementsize); + capwap_itemlist_insert_after(buildpacket->elementslist, NULL, itemlist); + + /* Next */ + pos += elementsize; + controlsize -= elementsize; + buffersize -= elementsize; + } + } else { + /* TODO */ + } + } + + return buildpacket; +} + +/* */ +unsigned long capwap_build_packet_validate(struct capwap_build_packet* buildpacket, capwap_unrecognized_element_array* reasonarray) { + unsigned short binding; + int ieee80211delta = CAPWAP_80211_MESSAGE_ELEMENTS_START - CAPWAP_MESSAGE_ELEMENTS_COUNT; + int elements[CAPWAP_MESSAGE_ELEMENTS_COUNT + CAPWAP_80211_MESSAGE_ELEMENTS_COUNT]; + struct capwap_list_item* item; + unsigned long result = CAPWAP_VALID_PACKET; + struct capwap_resultcode_element* resultcodeelement = NULL; + + ASSERT(buildpacket != NULL); + + /* Reset flags */ + memset(elements, 0, sizeof(elements)); + + /* Scan all elements */ + item = buildpacket->elementslist->first; + while (item != NULL) { + struct unrecognized_info info = { 0, 0 }; + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); \ + + if (f && f->check && f->parsing) { + if (f->check(elementitem)) { + if (IS_MESSAGE_ELEMENTS(type)) { + elements[type] = 1; + if (type == CAPWAP_ELEMENT_RESULTCODE) { + resultcodeelement = (struct capwap_resultcode_element*)f->parsing(elementitem); + } + } else if (IS_80211_MESSAGE_ELEMENTS(type)) { + elements[type - ieee80211delta] = 1; + } else { + /* Unknown message element */ + info.element = type; + info.reason = CAPWAP_REASON_UNKNOWN_MESSAGE_ELEMENT; + } + } else { + /* Invalid message element */ + info.element = type; + info.reason = CAPWAP_REASON_UNKNOWN_MESSAGE_ELEMENT_VALUE; + } + } else { + /* Unable parsing message element */ + info.element = type; + info.reason = CAPWAP_REASON_UNSUPPORTED_MESSAGE_ELEMENT; + } + + /* Copy error */ + if ((info.element != 0) && reasonarray) { + struct unrecognized_info* reasoninfo = capwap_array_get_item_pointer(reasonarray, reasonarray->count); + + memcpy(reasoninfo, &info, sizeof(struct unrecognized_info)); + result |= CAPWAP_UNRECOGNIZED_MSG_ELEMENT; + } + + /* Next item */ + item = item->next; + } + + /* Verify flags */ + binding = GET_WBID_HEADER(&buildpacket->header); + switch (ntohl(buildpacket->ctrlmsg.type)) { + case CAPWAP_DISCOVERY_REQUEST: { + if (elements[CAPWAP_ELEMENT_DISCOVERYTYPE] && + elements[CAPWAP_ELEMENT_WTPBOARDDATA] && + elements[CAPWAP_ELEMENT_WTPDESCRIPTOR] && + elements[CAPWAP_ELEMENT_WTPFRAMETUNNELMODE] && + elements[CAPWAP_ELEMENT_WTPMACTYPE]) { + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + if (!elements[CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION - ieee80211delta]) { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + } + } else { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + + break; + } + + case CAPWAP_DISCOVERY_RESPONSE: { + if (elements[CAPWAP_ELEMENT_ACDESCRIPTION] && + elements[CAPWAP_ELEMENT_ACNAME] && + (elements[CAPWAP_ELEMENT_CONTROLIPV4] || elements[CAPWAP_ELEMENT_CONTROLIPV6])) { + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + if (!elements[CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION - ieee80211delta]) { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + } + } else { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + + break; + } + + case CAPWAP_JOIN_REQUEST: { + if (elements[CAPWAP_ELEMENT_LOCATION] && + elements[CAPWAP_ELEMENT_WTPBOARDDATA] && + elements[CAPWAP_ELEMENT_WTPDESCRIPTOR] && + elements[CAPWAP_ELEMENT_WTPNAME] && + elements[CAPWAP_ELEMENT_SESSIONID] && + elements[CAPWAP_ELEMENT_WTPFRAMETUNNELMODE] && + elements[CAPWAP_ELEMENT_WTPMACTYPE] && + elements[CAPWAP_ELEMENT_ECNSUPPORT] && + (elements[CAPWAP_ELEMENT_LOCALIPV4] || elements[CAPWAP_ELEMENT_LOCALIPV6])) { + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + if (!elements[CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION - ieee80211delta]) { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + } + } else { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + + break; + } + + case CAPWAP_JOIN_RESPONSE: { + if (elements[CAPWAP_ELEMENT_RESULTCODE]) { + if ((resultcodeelement->code == CAPWAP_RESULTCODE_SUCCESS) || (resultcodeelement->code == CAPWAP_RESULTCODE_SUCCESS_NAT_DETECTED)) { + if (elements[CAPWAP_ELEMENT_ACDESCRIPTION] && + elements[CAPWAP_ELEMENT_ACNAME] && + elements[CAPWAP_ELEMENT_ECNSUPPORT] && + (elements[CAPWAP_ELEMENT_CONTROLIPV4] || elements[CAPWAP_ELEMENT_CONTROLIPV6]) && + (elements[CAPWAP_ELEMENT_LOCALIPV4] || elements[CAPWAP_ELEMENT_LOCALIPV6])) { + + if (binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + if (!elements[CAPWAP_ELEMENT_80211_WTPRADIOINFORMATION - ieee80211delta]) { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + } + } else { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + } else if (resultcodeelement->code == CAPWAP_RESULTCODE_FAILURE_UNRECOGNIZED_MESSAGE_ELEMENT) { + if (!elements[CAPWAP_ELEMENT_RETURNEDMESSAGE]) { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + } + } else { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + + break; + } + + case CAPWAP_CONFIGURATION_STATUS_REQUEST: { + if (elements[CAPWAP_ELEMENT_ACNAME] && + elements[CAPWAP_ELEMENT_RADIOADMSTATE] && + elements[CAPWAP_ELEMENT_STATISTICSTIMER] && + elements[CAPWAP_ELEMENT_WTPREBOOTSTAT]) { + /* TODO binding */ + } else { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + + break; + } + + case CAPWAP_CONFIGURATION_STATUS_RESPONSE: { + if (elements[CAPWAP_ELEMENT_TIMERS] && + elements[CAPWAP_ELEMENT_DECRYPTERRORREPORTPERIOD] && + elements[CAPWAP_ELEMENT_IDLETIMEOUT] && + elements[CAPWAP_ELEMENT_WTPFALLBACK] && + (elements[CAPWAP_ELEMENT_ACIPV4LIST] || elements[CAPWAP_ELEMENT_ACIPV6LIST])) { + /* TODO binding */ + } else { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + + break; + } + + case CAPWAP_CONFIGURATION_UPDATE_REQUEST: { + break; + } + + case CAPWAP_CONFIGURATION_UPDATE_RESPONSE: { + break; + } + + case CAPWAP_WTP_EVENT_REQUEST: { + break; + } + + case CAPWAP_WTP_EVENT_RESPONSE: { + break; + } + + case CAPWAP_CHANGE_STATE_EVENT_REQUEST: { + if (elements[CAPWAP_ELEMENT_RADIOOPRSTATE] && + elements[CAPWAP_ELEMENT_RESULTCODE]) { + /* TODO binding */ + } else { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + break; + } + + case CAPWAP_CHANGE_STATE_EVENT_RESPONSE: { + /* TODO binding */ + break; + } + + case CAPWAP_ECHO_REQUEST: { + break; + } + + case CAPWAP_ECHO_RESPONSE: { + break; + } + + case CAPWAP_IMAGE_DATA_REQUEST: { + break; + } + + case CAPWAP_IMAGE_DATA_RESPONSE: { + break; + } + + case CAPWAP_RESET_REQUEST: { + if (!elements[CAPWAP_ELEMENT_IMAGEIDENTIFIER]) { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + + break; + } + + case CAPWAP_RESET_RESPONSE: { + if (!elements[CAPWAP_ELEMENT_RESULTCODE]) { + result |= CAPWAP_MISSING_MANDATORY_MSG_ELEMENT; + } + + break; + } + + case CAPWAP_PRIMARY_DISCOVERY_REQUEST: { + break; + } + + case CAPWAP_PRIMARY_DISCOVERY_RESPONSE: { + break; + } + + case CAPWAP_DATA_TRANSFER_REQUEST: { + break; + } + + case CAPWAP_DATA_TRANSFER_RESPONSE: { + break; + } + + case CAPWAP_CLEAR_CONFIGURATION_REQUEST: { + break; + } + + case CAPWAP_CLEAR_CONFIGURATION_RESPONSE: { + break; + } + + case CAPWAP_STATION_CONFIGURATION_REQUEST: { + break; + } + + case CAPWAP_STATION_CONFIGURATION_RESPONSE: { + break; + } + } + + if (resultcodeelement) { + capwap_get_message_element(CAPWAP_ELEMENT_RESULTCODE)->free(resultcodeelement); + } + + return result; +} + +/* Detect if type is a request */ +int capwap_is_request_type(unsigned long type) { + if ((type == CAPWAP_DISCOVERY_REQUEST) || + (type == CAPWAP_JOIN_REQUEST) || + (type == CAPWAP_CONFIGURATION_STATUS_REQUEST) || + (type == CAPWAP_CONFIGURATION_UPDATE_REQUEST) || + (type == CAPWAP_WTP_EVENT_REQUEST) || + (type == CAPWAP_CHANGE_STATE_EVENT_REQUEST) || + (type == CAPWAP_ECHO_REQUEST) || + (type == CAPWAP_IMAGE_DATA_REQUEST) || + (type == CAPWAP_RESET_REQUEST) || + (type == CAPWAP_PRIMARY_DISCOVERY_REQUEST) || + (type == CAPWAP_DATA_TRANSFER_REQUEST) || + (type == CAPWAP_CLEAR_CONFIGURATION_REQUEST) || + (type == CAPWAP_STATION_CONFIGURATION_REQUEST)) { + + /* Request type */ + return 1; + } + + return 0; +} + +/* Retrieve packet digest */ +void capwap_get_packet_digest(void* buffer, unsigned long length, unsigned char packetdigest[16]) { + MD5_CTX mdContext; + + ASSERT(buffer != NULL); + ASSERT(length > 0); + + MD5Init(&mdContext); + MD5Update(&mdContext, (unsigned char*)buffer, length); + MD5Final(&mdContext); + + memcpy(&packetdigest[0], &mdContext.digest[0], sizeof(unsigned char) * 16); +} + +/* Verify duplicate packet */ +int capwap_recv_retrasmitted_request(struct capwap_dtls* dtls, struct capwap_packet* packet, unsigned char lastseqnumber, unsigned char packetdigest[16], struct capwap_socket* sock, capwap_fragment_packet_array* txfragmentpacket, struct sockaddr_storage* sendfromaddr, struct sockaddr_storage* sendtoaddr) { + unsigned char recvpacketdigest[16]; + unsigned short lengthpayload; + + ASSERT(packet != NULL); + ASSERT(sock != NULL); + ASSERT(txfragmentpacket != NULL); + ASSERT(sendtoaddr != NULL); + + lengthpayload = packet->packetsize - GET_HLEN_HEADER(packet->header) * 4; + if (lengthpayload >= sizeof(struct capwap_control_message)) { + struct capwap_control_message* ctrlmsg = (struct capwap_control_message*)packet->payload; + + /* Check if request */ + if (capwap_is_request_type(ntohl(ctrlmsg->type)) && (ctrlmsg->seq == lastseqnumber)) { + /* Check packet digest */ + capwap_get_packet_digest((void*)packet->header, packet->packetsize, recvpacketdigest); + if (!memcmp(&recvpacketdigest[0], &packetdigest[0], sizeof(unsigned char) * 16)) { + int i; + + /* Retransmit response */ + for (i = 0; i < txfragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(txfragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(dtls, sock->socket[sock->type], txpacket->header, txpacket->packetsize, sendfromaddr, sendtoaddr)) { + capwap_logging_debug("Warning: error to resend response packet"); + break; + } + } + + return 1; + } + } + } + + return 0; +} + +/* Check valid message type */ +int capwap_check_message_type(struct capwap_dtls* dtls, struct capwap_packet* packet, unsigned short mtu) { + unsigned short lengthpayload; + + ASSERT(packet != NULL); + ASSERT(mtu > 0); + + lengthpayload = packet->packetsize - GET_HLEN_HEADER(packet->header) * 4; + if (lengthpayload >= sizeof(struct capwap_control_message)) { + struct capwap_control_message* ctrlmsg = (struct capwap_control_message*)packet->payload; + unsigned long type = ntohl(ctrlmsg->type); + + if ((type >= CAPWAP_FIRST_MESSAGE_TYPE) && (type <= CAPWAP_LAST_MESSAGE_TYPE)) { + return 1; + } + + /* Unknown message type */ + if ((type % 2) != 0) { + int i; + struct capwap_build_packet* responsepacket; + struct capwap_resultcode_element resultcode = { CAPWAP_RESULTCODE_MSG_UNEXPECTED_UNRECOGNIZED_REQUEST }; + capwap_fragment_packet_array* txfragmentpacket = NULL; + + /* Odd message type, response with "Unrecognized Request" */ + responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, GET_WBID_HEADER(packet->header)); + responsepacket->isctrlmsg = 1; + capwap_build_packet_set_control_message_type(responsepacket, type + 1, ctrlmsg->seq); + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_RESULTCODE_ELEMENT(&resultcode)); + + txfragmentpacket = capwap_array_create(sizeof(struct capwap_packet), 0); + if (capwap_fragment_build_packet(responsepacket, txfragmentpacket, mtu, 0) >= 0) { + for (i = 0; i < txfragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(txfragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(dtls, packet->socket.socket[packet->socket.type], txpacket->header, txpacket->packetsize, &packet->remoteaddr, &packet->remoteaddr)) { + break; + } + } + } + + /* Free memory */ + capwap_fragment_free(txfragmentpacket); + capwap_array_free(txfragmentpacket); + capwap_build_packet_free(responsepacket); + } + } + + return 0; +} + +int capwap_get_sessionid_from_keepalive(struct capwap_build_packet* buildpacket, struct capwap_sessionid_element* session) { + int found = 0; + + ASSERT(buildpacket != NULL); + ASSERT(session != NULL); + + /* Check is Data Packet KeepAlive */ + if (IS_FLAG_K_HEADER(&buildpacket->header) && !capwap_build_packet_validate(buildpacket, NULL)) { + struct capwap_list_item* item = buildpacket->elementslist->first; + + while (!found && (item != NULL)) { + struct capwap_message_element* elementitem = (struct capwap_message_element*)item->item; + unsigned short type = ntohs(elementitem->type); + struct capwap_message_elements_func* f = capwap_get_message_element(type); + + ASSERT(f != NULL); + ASSERT(f->parsing != NULL); + + switch (type) { + case CAPWAP_ELEMENT_SESSIONID: { + struct capwap_sessionid_element* tempsession; + + tempsession = (struct capwap_sessionid_element*)f->parsing(elementitem); + memcpy(session, tempsession, sizeof(struct capwap_sessionid_element)); + f->free(tempsession); + + found = 1; + break; + } + } + + /* Next element */ + item = item->next; + } + } + + return found; +} diff --git a/src/common/capwap_protocol.h b/src/common/capwap_protocol.h new file mode 100644 index 0000000..bd19b5f --- /dev/null +++ b/src/common/capwap_protocol.h @@ -0,0 +1,278 @@ +#ifndef __CAPWAP_PROTOCOL_HEADER__ +#define __CAPWAP_PROTOCOL_HEADER__ + +#include "capwap_element.h" +#include "capwap_network.h" +#include "capwap_dtls.h" + +#define CAPWAP_PROTOCOL_VERSION 0 + +#define CAPWAP_MTU_DEFAULT 1400 +#define CAPWAP_DONT_FRAGMENT 0 + +/* Capwap preamble */ +#define CAPWAP_PREAMBLE_HEADER 0 +#define CAPWAP_PREAMBLE_DTLS_HEADER 1 + +struct capwap_preamble { +#ifdef CAPWAP_BIG_ENDIAN + unsigned char version : 4; + unsigned char type : 4; +#else + unsigned char type : 4; + unsigned char version : 4; +#endif +} __attribute__((__packed__)); + +/* Capwap DTLS header */ +struct capwap_dtls_header { + struct capwap_preamble preamble; + unsigned char reserved1; + unsigned char reserved2; + unsigned char reserved3; +} __attribute__((__packed__)); + +/* Capwap header: 8 (header) + 12 (radio mac) + 256 (wireless info) */ +#define CAPWAP_HEADER_MAX_SIZE 276 +struct capwap_header { + struct capwap_preamble preamble; +#ifdef CAPWAP_BIG_ENDIAN + unsigned short hlen : 5; + unsigned short rid : 5; + unsigned short wbid : 5; + unsigned short flag_t : 1; + unsigned char flag_f : 1; + unsigned char flag_l : 1; + unsigned char flag_w : 1; + unsigned char flag_m : 1; + unsigned char flag_k : 1; + unsigned char flag_res : 3; +#else + unsigned short _rid_hi : 3; + unsigned short hlen : 5; + unsigned short flag_t : 1; + unsigned short wbid : 5; + unsigned short _rid_lo : 2; + unsigned char flag_res : 3; + unsigned char flag_k : 1; + unsigned char flag_m : 1; + unsigned char flag_w : 1; + unsigned char flag_l : 1; + unsigned char flag_f : 1; +#endif + unsigned short frag_id; + unsigned short frag_off; /* Only first 13 bit */ +} __attribute__((__packed__)); + +#define FRAGMENT_OFFSET_MASK 0xfff8 + +/* Mac Address */ +struct capwap_mac_address { + unsigned char length; + char address[0]; +} __attribute__((__packed__)); + +/* Wireless Information */ +struct capwap_wireless_information { + unsigned char length; + char data[0]; +} __attribute__((__packed__)); + +/* Control Message Type */ +#define CAPWAP_FIRST_MESSAGE_TYPE 1 +#define CAPWAP_DISCOVERY_REQUEST 1 +#define CAPWAP_DISCOVERY_RESPONSE 2 +#define CAPWAP_JOIN_REQUEST 3 +#define CAPWAP_JOIN_RESPONSE 4 +#define CAPWAP_CONFIGURATION_STATUS_REQUEST 5 +#define CAPWAP_CONFIGURATION_STATUS_RESPONSE 6 +#define CAPWAP_CONFIGURATION_UPDATE_REQUEST 7 +#define CAPWAP_CONFIGURATION_UPDATE_RESPONSE 8 +#define CAPWAP_WTP_EVENT_REQUEST 9 +#define CAPWAP_WTP_EVENT_RESPONSE 10 +#define CAPWAP_CHANGE_STATE_EVENT_REQUEST 11 +#define CAPWAP_CHANGE_STATE_EVENT_RESPONSE 12 +#define CAPWAP_ECHO_REQUEST 13 +#define CAPWAP_ECHO_RESPONSE 14 +#define CAPWAP_IMAGE_DATA_REQUEST 15 +#define CAPWAP_IMAGE_DATA_RESPONSE 16 +#define CAPWAP_RESET_REQUEST 17 +#define CAPWAP_RESET_RESPONSE 18 +#define CAPWAP_PRIMARY_DISCOVERY_REQUEST 19 +#define CAPWAP_PRIMARY_DISCOVERY_RESPONSE 20 +#define CAPWAP_DATA_TRANSFER_REQUEST 21 +#define CAPWAP_DATA_TRANSFER_RESPONSE 22 +#define CAPWAP_CLEAR_CONFIGURATION_REQUEST 23 +#define CAPWAP_CLEAR_CONFIGURATION_RESPONSE 24 +#define CAPWAP_STATION_CONFIGURATION_REQUEST 25 +#define CAPWAP_STATION_CONFIGURATION_RESPONSE 26 +#define CAPWAP_LAST_MESSAGE_TYPE 26 + +/* Control Message */ +struct capwap_control_message { + unsigned long type; + unsigned char seq; + unsigned short length; + unsigned char flags; + char elements[0]; +} __attribute__((__packed__)); + +/* Data Message */ +struct capwap_data_message { + unsigned short length; + char elements[0]; +} __attribute__((__packed__)); + +/* Capwap dtls header helper */ +#define GET_DTLS_BODY(x) (void*)(((char*)(x)) + sizeof(struct capwap_dtls_header)) + +/* Capwap header helper */ +#define GET_VERSION_HEADER(x) ((x)->preamble.version) +#define SET_VERSION_HEADER(x, y) ((x)->preamble.version = (unsigned char)(y)) +#define GET_TYPE_HEADER(x) ((x)->preamble.type) +#define SET_TYPE_HEADER(x, y) ((x)->preamble.type = (unsigned char)(y)) + +#define GET_HLEN_HEADER(x) ((x)->hlen) +#define SET_HLEN_HEADER(x, y) ((x)->hlen = (unsigned short)(y)) +#ifdef CAPWAP_BIG_ENDIAN + #define GET_RID_HEADER(x) ((x)->rid) + #define SET_RID_HEADER(x, y) ((x)->rid = (unsigned short)(y)) +#else + #define GET_RID_HEADER(x) ((unsigned short)((x)->_rid_hi << 2 | (x)->_rid_lo)) + #define SET_RID_HEADER(x, y) ({ (x)->_rid_hi = (unsigned short)((y) >> 2); (x)->_rid_lo = (unsigned short)((y) & 0x0003); }) +#endif +#define GET_WBID_HEADER(x) ((x)->wbid) +#define SET_WBID_HEADER(x, y) ((x)->wbid = (unsigned short)(y)) + +#define IS_FLAG_T_HEADER(x) ((x)->flag_t) +#define SET_FLAG_T_HEADER(x, y) ((x)->flag_t = ((y) ? 1 : 0)) +#define IS_FLAG_F_HEADER(x) ((x)->flag_f) +#define SET_FLAG_F_HEADER(x, y) ((x)->flag_f = ((y) ? 1 : 0)) +#define IS_FLAG_L_HEADER(x) ((x)->flag_l) +#define SET_FLAG_L_HEADER(x, y) ((x)->flag_l = ((y) ? 1 : 0)) +#define IS_FLAG_W_HEADER(x) ((x)->flag_w) +#define SET_FLAG_W_HEADER(x, y) ((x)->flag_w = ((y) ? 1 : 0)) +#define IS_FLAG_M_HEADER(x) ((x)->flag_m) +#define SET_FLAG_M_HEADER(x, y) ((x)->flag_m = ((y) ? 1 : 0)) +#define IS_FLAG_K_HEADER(x) ((x)->flag_k) +#define SET_FLAG_K_HEADER(x, y) ((x)->flag_k = ((y) ? 1 : 0)) + +#define GET_FRAGMENT_ID_HEADER(x) (ntohs((x)->frag_id)) +#define SET_FRAGMENT_ID_HEADER(x, y) ((x)->frag_id = htons((unsigned short)(y))) +#define GET_FRAGMENT_OFFSET_HEADER(x) (ntohs((x)->frag_off) & FRAGMENT_OFFSET_MASK) +#define SET_FRAGMENT_OFFSET_HEADER(x, y) ((x)->frag_off &= ~FRAGMENT_OFFSET_MASK, (x)->frag_off |= htons((unsigned short)(y) & FRAGMENT_OFFSET_MASK)) + +#define GET_RADIO_MAC_ADDRESS_STRUCT(x) ((struct capwap_mac_address*)(((char*)(x)) + sizeof(struct capwap_header))) +#define GET_WIRELESS_INFORMATION_STRUCT(x) ((struct capwap_wireless_information*)(((char*)(x)) + sizeof(struct capwap_header) + (IS_FLAG_M_HEADER(x) ? (((GET_RADIO_MAC_ADDRESS_STRUCT(x)->length + sizeof(struct capwap_mac_address)) + 3) / 4) * 4 : 0))) +#define GET_PAYLOAD_HEADER(x) ((void*)(((char*)(x)) + GET_HLEN_HEADER(x) * 4)) + +#define IS_SEQUENCE_SMALLER(s1, s2) (((((s1) < (s2)) && (((s2) - (s1)) < 128)) || (((s1) > (s2)) && (((s1) - (s2)) > 128))) ? 1 : 0) + +/*********************************************************************************************************************/ +/* Sanity check packet */ +#define CAPWAP_WRONG_PACKET -1 +#define CAPWAP_NONE_PACKET 0 +#define CAPWAP_PLAIN_PACKET 1 +#define CAPWAP_DTLS_PACKET 2 +int capwap_sanity_check(int isctrlsocket, int state, void* buffer, int buffersize, int dtlsctrlenable, int dtlsdataenable); + +/* Fragment packet */ +struct capwap_fragment_packet { + void* buffer; + int size; + unsigned short offset; +}; + +/* Fragment control list */ +struct capwap_fragment_sender { + struct sockaddr_storage sendaddr; + unsigned short fragment_id; + int islastrecv; + + /* Packet */ + struct capwap_header* header; + struct capwap_list* packetlist; +}; + +typedef struct capwap_list capwap_fragment_list; + +/* Packet */ +struct capwap_packet { + unsigned short packetsize; + struct capwap_header* header; + void* payload; + struct sockaddr_storage localaddr; + struct sockaddr_storage remoteaddr; + struct capwap_socket socket; +}; + +#define CAPWAP_WRONG_FRAGMENT -1 +#define CAPWAP_REQUEST_MORE_FRAGMENT 0 +#define CAPWAP_RECEIVE_COMPLETE_PACKET 1 +int capwap_defragment_packets(struct sockaddr_storage* sendaddr, void* buffer, int buffersize, capwap_fragment_list* defraglist, struct capwap_packet* packet); + +capwap_fragment_list* capwap_defragment_init_list(void); +void capwap_defragment_flush_list(capwap_fragment_list* defraglist); +void capwap_defragment_free_list(capwap_fragment_list* defraglist); + +int capwap_defragment_remove_sender(capwap_fragment_list* defraglist, struct sockaddr_storage* sendaddr); +struct capwap_fragment_sender* capwap_defragment_find_sender(capwap_fragment_list* defraglist, struct sockaddr_storage* sendaddr); + +void capwap_free_packet(struct capwap_packet* packet); + +/* Build tx packet */ +struct capwap_build_packet { + union { + struct capwap_header header; + char headerbuffer[CAPWAP_HEADER_MAX_SIZE]; + }; + + /* Control Packet */ + int isctrlmsg; + union { + struct capwap_control_message ctrlmsg; + struct capwap_data_message datamsg; + }; + + /* Message element */ + struct capwap_list* elementslist; +}; + +#define CAPWAP_RADIOID_NONE 0 +#define CAPWAP_WIRELESS_BINDING_NONE 0 +#define CAPWAP_WIRELESS_BINDING_IEEE80211 1 +#define CAPWAP_WIRELESS_BINDING_EPCGLOBAL 3 + +struct capwap_build_packet* capwap_tx_packet_create(unsigned short radioid, unsigned short binding); +struct capwap_build_packet* capwap_rx_packet_create(void* buffer, int buffersize, int isctrlpacket); +void capwap_build_packet_free(struct capwap_build_packet* buildpacket); +void capwap_build_packet_set_radio_macaddress(struct capwap_build_packet* buildpacket, int radiotype, char* macaddress); +void capwap_build_packet_set_wireless_information(struct capwap_build_packet* buildpacket, void* buffer, unsigned char length); +void capwap_build_packet_set_control_message_type(struct capwap_build_packet* buildpacket, unsigned long type, unsigned char seq); +void capwap_build_packet_add_message_element(struct capwap_build_packet* buildpacket, struct capwap_message_element* element); + +#define CAPWAP_VALID_PACKET 0x00000000 +#define CAPWAP_MISSING_MANDATORY_MSG_ELEMENT 0x00000001 +#define CAPWAP_UNRECOGNIZED_MSG_ELEMENT 0x00000002 + +struct unrecognized_info { + unsigned short element; + unsigned char reason; +}; + +typedef struct capwap_array capwap_unrecognized_element_array; +unsigned long capwap_build_packet_validate(struct capwap_build_packet* buildpacket, capwap_unrecognized_element_array* reasonarray); + +typedef struct capwap_array capwap_fragment_packet_array; +int capwap_fragment_build_packet(struct capwap_build_packet* buildpacket, capwap_fragment_packet_array* packets, unsigned short mtu, unsigned short fragmentid); +void capwap_fragment_free(capwap_fragment_packet_array* packets); + +int capwap_is_request_type(unsigned long type); +void capwap_get_packet_digest(void* buffer, unsigned long length, unsigned char packetdigest[16]); +int capwap_recv_retrasmitted_request(struct capwap_dtls* dtls, struct capwap_packet* packet, unsigned char lastseqnumber, unsigned char packetdigest[16], struct capwap_socket* sock, capwap_fragment_packet_array* txfragmentpacket, struct sockaddr_storage* sendfromaddr, struct sockaddr_storage* sendtoaddr); + +int capwap_check_message_type(struct capwap_dtls* dtls, struct capwap_packet* packet, unsigned short mtu); + +int capwap_get_sessionid_from_keepalive(struct capwap_build_packet* buildpacket, struct capwap_sessionid_element* session); + +#endif /* __CAPWAP_PROTOCOL_HEADER__ */ diff --git a/src/common/md5.c b/src/common/md5.c new file mode 100644 index 0000000..1b80da9 --- /dev/null +++ b/src/common/md5.c @@ -0,0 +1,264 @@ +/* + ********************************************************************** + ** md5.c ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +/* -- include the following line if the md5.h header file is separate -- */ +#include "md5.h" + +/* forward declaration */ +static void Transform(UINT4* buf, UINT4* in); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G and H are basic MD5 functions: selection, majority, parity */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +void MD5Init(MD5_CTX* mdContext) +{ + mdContext->i[0] = mdContext->i[1] = (UINT4)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (UINT4)0x67452301; + mdContext->buf[1] = (UINT4)0xefcdab89; + mdContext->buf[2] = (UINT4)0x98badcfe; + mdContext->buf[3] = (UINT4)0x10325476; +} + +void MD5Update(MD5_CTX* mdContext, unsigned char* inBuf, unsigned int inLen) +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((UINT4)inLen << 3); + mdContext->i[1] += ((UINT4)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +void MD5Final(MD5_CTX* mdContext) +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } +} + +/* Basic MD5 step. Transform buf based on in. + */ +static void Transform(UINT4* buf, UINT4* in) +{ + UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + + FF ( a, b, c, d, in[ 0], S11, 0xd76aa478); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, 0xe8c7b756); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, 0x242070db); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, 0xc1bdceee); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, 0xf57c0faf); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, 0x4787c62a); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, 0xa8304613); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, 0xfd469501); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, 0x698098d8); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, 0x8b44f7af); /* 10 */ + FF ( c, d, a, b, in[10], S13, 0xffff5bb1); /* 11 */ + FF ( b, c, d, a, in[11], S14, 0x895cd7be); /* 12 */ + FF ( a, b, c, d, in[12], S11, 0x6b901122); /* 13 */ + FF ( d, a, b, c, in[13], S12, 0xfd987193); /* 14 */ + FF ( c, d, a, b, in[14], S13, 0xa679438e); /* 15 */ + FF ( b, c, d, a, in[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, 0xf61e2562); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, 0xc040b340); /* 18 */ + GG ( c, d, a, b, in[11], S23, 0x265e5a51); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, 0xd62f105d); /* 21 */ + GG ( d, a, b, c, in[10], S22, 0x02441453); /* 22 */ + GG ( c, d, a, b, in[15], S23, 0xd8a1e681); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, 0x21e1cde6); /* 25 */ + GG ( d, a, b, c, in[14], S22, 0xc33707d6); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, 0xf4d50d87); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, 0x455a14ed); /* 28 */ + GG ( a, b, c, d, in[13], S21, 0xa9e3e905); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, 0xfcefa3f8); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, 0x676f02d9); /* 31 */ + GG ( b, c, d, a, in[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, 0xfffa3942); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, 0x8771f681); /* 34 */ + HH ( c, d, a, b, in[11], S33, 0x6d9d6122); /* 35 */ + HH ( b, c, d, a, in[14], S34, 0xfde5380c); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, 0xa4beea44); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, 0x4bdecfa9); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, 0xf6bb4b60); /* 39 */ + HH ( b, c, d, a, in[10], S34, 0xbebfbc70); /* 40 */ + HH ( a, b, c, d, in[13], S31, 0x289b7ec6); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, 0xeaa127fa); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, 0xd4ef3085); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, 0x04881d05); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, 0xd9d4d039); /* 45 */ + HH ( d, a, b, c, in[12], S32, 0xe6db99e5); /* 46 */ + HH ( c, d, a, b, in[15], S33, 0x1fa27cf8); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, 0xf4292244); /* 49 */ + II ( d, a, b, c, in[ 7], S42, 0x432aff97); /* 50 */ + II ( c, d, a, b, in[14], S43, 0xab9423a7); /* 51 */ + II ( b, c, d, a, in[ 5], S44, 0xfc93a039); /* 52 */ + II ( a, b, c, d, in[12], S41, 0x655b59c3); /* 53 */ + II ( d, a, b, c, in[ 3], S42, 0x8f0ccc92); /* 54 */ + II ( c, d, a, b, in[10], S43, 0xffeff47d); /* 55 */ + II ( b, c, d, a, in[ 1], S44, 0x85845dd1); /* 56 */ + II ( a, b, c, d, in[ 8], S41, 0x6fa87e4f); /* 57 */ + II ( d, a, b, c, in[15], S42, 0xfe2ce6e0); /* 58 */ + II ( c, d, a, b, in[ 6], S43, 0xa3014314); /* 59 */ + II ( b, c, d, a, in[13], S44, 0x4e0811a1); /* 60 */ + II ( a, b, c, d, in[ 4], S41, 0xf7537e82); /* 61 */ + II ( d, a, b, c, in[11], S42, 0xbd3af235); /* 62 */ + II ( c, d, a, b, in[ 2], S43, 0x2ad7d2bb); /* 63 */ + II ( b, c, d, a, in[ 9], S44, 0xeb86d391); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} diff --git a/src/common/md5.h b/src/common/md5.h new file mode 100644 index 0000000..cbe405b --- /dev/null +++ b/src/common/md5.h @@ -0,0 +1,53 @@ +/* + ********************************************************************** + ** md5.h -- Header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +/* typedef a 32 bit type */ +typedef unsigned long int UINT4; + +/* Data structure for MD5 (Message Digest) computation */ +typedef struct { + UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ + UINT4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init(MD5_CTX* mdContext); +void MD5Update(MD5_CTX* mdContext, unsigned char* inBuf, unsigned int inLen); +void MD5Final(MD5_CTX* mdContext); diff --git a/src/common/startap.txt b/src/common/startap.txt new file mode 100644 index 0000000..4e1bc1e --- /dev/null +++ b/src/common/startap.txt @@ -0,0 +1,13 @@ +init +deinit +getcapability +setcountry +setfrequency +setrts +setfragment +settxqueue +addap +deleteap +addstation +removestation +getstat \ No newline at end of file diff --git a/src/wtp/wtp.c b/src/wtp/wtp.c new file mode 100644 index 0000000..725840c --- /dev/null +++ b/src/wtp/wtp.c @@ -0,0 +1,963 @@ +#include "wtp.h" +#include "capwap_network.h" +#include "capwap_protocol.h" +#include "capwap_dfa.h" +#include "capwap_array.h" +#include "capwap_element.h" +#include "capwap_dtls.h" +#include "wtp_dfa.h" + +#include +#include + +struct wtp_t g_wtp; + +/* Local param */ +#define WTP_STANDARD_NAME "Unknown WTP" +#define WTP_STANDARD_LOCATION "Unknown Location" + +static char g_configurationfile[260] = WTP_STANDARD_CONFIGURATION_FILE; + +/* Alloc WTP */ +static int wtp_init(void) { + /* Init WTP with default value */ + memset(&g_wtp, 0, sizeof(struct wtp_t)); + + /* Standard name */ + strcpy(g_wtp.name.name, WTP_STANDARD_NAME); + strcpy(g_wtp.location.value, WTP_STANDARD_LOCATION); + + /* State machine */ + g_wtp.dfa.state = CAPWAP_START_STATE; + g_wtp.dfa.rfcMaxDiscoveryInterval = WTP_DEFAULT_DISCOVERY_INTERVAL; + g_wtp.dfa.rfcMaxDiscoveries = WTP_DEFAULT_DISCOVERY_COUNT; + g_wtp.dfa.rfcSilentInterval = WTP_DEFAULT_SILENT_INTERVAL; + g_wtp.dfa.rfcRetransmitInterval = WTP_DEFAULT_RETRANSMIT_INTERVAL; + g_wtp.dfa.rfcMaxRetransmit = WTP_MAX_RETRANSMIT; + g_wtp.dfa.rfcWaitDTLS = WTP_DEFAULT_WAITDTLS_INTERVAL; + g_wtp.dfa.rfcDataChannelKeepAlive = WTP_DEFAULT_DATACHANNEL_KEEPALIVE; + g_wtp.dfa.rfcDataChannelDeadInterval = WTP_DEFAULT_DATACHANNEL_KEEPALIVEDEAD; + g_wtp.dfa.rfcEchoInterval = WTP_DEFAULT_ECHO_INTERVAL; + g_wtp.dfa.rfcDTLSSessionDelete = WTP_DEFAULT_DTLS_SESSION_DELETE; + g_wtp.dfa.rfcMaxFailedDTLSSessionRetry = WTP_DEFAULT_FAILED_DTLS_SESSION_RETRY; + + /* Socket */ + capwap_network_init(&g_wtp.net); + + /* Standard configuration */ + g_wtp.boarddata.boardsubelement = capwap_array_create(sizeof(struct capwap_wtpboarddata_board_subelement), 0); + g_wtp.descriptor.encryptsubelement = capwap_array_create(sizeof(struct capwap_wtpdescriptor_encrypt_subelement), 0); + g_wtp.descriptor.descsubelement = capwap_array_create(sizeof(struct capwap_wtpdescriptor_desc_subelement), 0); + + g_wtp.binding = CAPWAP_WIRELESS_BINDING_NONE; + + g_wtp.ecn.flag = CAPWAP_LIMITED_ECN_SUPPORT; + g_wtp.transport.type = CAPWAP_UDP_TRANSPORT; + g_wtp.statisticstimer.timer = WTP_DEFAULT_STATISTICSTIMER_INTERVAL; + + g_wtp.mactype.type = CAPWAP_LOCALMAC; + g_wtp.mactunnel.mode = CAPWAP_WTP_LOCAL_BRIDGING; + + /* DTLS */ + g_wtp.validdtlsdatapolicy = CAPWAP_ACDESC_CLEAR_DATA_CHANNEL_ENABLED; + + /* Tx fragment packets */ + g_wtp.mtu = CAPWAP_MTU_DEFAULT; + g_wtp.requestfragmentpacket = capwap_array_create(sizeof(struct capwap_packet), 0); + g_wtp.responsefragmentpacket = capwap_array_create(sizeof(struct capwap_packet), 0); + + /* AC information */ + g_wtp.discoverytype.type = CAPWAP_ELEMENT_DISCOVERYTYPE_TYPE_UNKNOWN; + g_wtp.acdiscoveryrequest = 1; + g_wtp.acdiscoveryarray = capwap_array_create(sizeof(struct sockaddr_storage), 0); + g_wtp.acpreferedarray = capwap_array_create(sizeof(struct sockaddr_storage), 0); + g_wtp.acdiscoveryresponse = capwap_array_create(sizeof(struct wtp_discovery_response), 0); + + /* Radios */ + g_wtp.radios = capwap_array_create(sizeof(struct wtp_radio), 0); + + return 1; +} + +/* Destroy WTP */ +static void wtp_destroy(void) { + /* Dtls */ + capwap_crypt_freecontext(&g_wtp.dtlscontext); + + /* Free standard configuration */ + capwap_array_free(g_wtp.descriptor.encryptsubelement); + capwap_array_free(g_wtp.descriptor.descsubelement); + capwap_array_free(g_wtp.boarddata.boardsubelement); + + /* Free fragments packet */ + capwap_fragment_free(g_wtp.requestfragmentpacket); + capwap_fragment_free(g_wtp.responsefragmentpacket); + capwap_array_free(g_wtp.requestfragmentpacket); + capwap_array_free(g_wtp.responsefragmentpacket); + + /* Free list AC */ + capwap_array_free(g_wtp.acdiscoveryarray); + capwap_array_free(g_wtp.acpreferedarray); + + wtp_free_discovery_response_array(); + capwap_array_free(g_wtp.acdiscoveryresponse); + + /* Free radios */ + capwap_array_free(g_wtp.radios); +} + +/* Save AC address */ +static int wtp_add_acaddress(struct sockaddr_storage* source, struct capwap_array* array) { + ASSERT(source != NULL); + ASSERT(array != NULL); + + if ((g_wtp.net.sock_family == AF_UNSPEC) || (g_wtp.net.sock_family == source->ss_family)) { + struct sockaddr_storage* destaddr = (struct sockaddr_storage*)capwap_array_get_item_pointer(array, array->count); + + /* Save address, if request, mapping IPv4 to IPv6 */ + if ((g_wtp.net.sock_family == AF_UNSPEC) && (source->ss_family == AF_INET) && !(g_wtp.net.bind_ctrl_flags & CAPWAP_IPV6ONLY_FLAG)) { + if (!capwap_ipv4_mapped_ipv6(source, destaddr)) { + memcpy(destaddr, source, sizeof(struct sockaddr_storage)); + } + } else { + memcpy(destaddr, source, sizeof(struct sockaddr_storage)); + } + + return 1; + } + + return 0; +} + +/* */ +static int wtp_add_default_acaddress() { + struct sockaddr_storage address; + struct sockaddr_in* addressv4 = (struct sockaddr_in*)&address; + /*struct sockaddr_in6* addressv6 = (struct sockaddr_in6*)&address;*/ + + /* Broadcast IPv4 */ + addressv4->sin_family = AF_INET; + addressv4->sin_addr.s_addr = INADDR_BROADCAST; + addressv4->sin_port = htons(CAPWAP_CONTROL_PORT); + wtp_add_acaddress(&address, g_wtp.acdiscoveryarray); + + /* Multicast IPv4 */ + /* TODO */ + + /* Multicast IPv6 */ + /* TODO */ + + return ((g_wtp.acdiscoveryarray->count > 0) ? 1 : 0); +} + +/* Help */ +static void wtp_print_usage(void) { + /* TODO */ +} + +/* Parsing configuration */ +static int wtp_parsing_configuration_1_0(config_t* config) { + int i; + int configInt; + int configIPv4; + int configIPv6; + long int configLongInt; + const char* configString; + config_setting_t* configSetting; + + /* Logging configuration */ + if (config_lookup_bool(config, "logging.enable", &configInt) == CONFIG_TRUE) { + if (!configInt) { + capwap_logging_verboselevel(CAPWAP_LOGGING_NONE); + capwap_logging_disable_allinterface(); + } else { + if (config_lookup_string(config, "logging.level", &configString) == CONFIG_TRUE) { + if (!strcmp(configString, "fatal")) { + capwap_logging_verboselevel(CAPWAP_LOGGING_FATAL); + } else if (!strcmp(configString, "error")) { + capwap_logging_verboselevel(CAPWAP_LOGGING_ERROR); + } else if (!strcmp(configString, "warning")) { + capwap_logging_verboselevel(CAPWAP_LOGGING_WARNING); + } else if (!strcmp(configString, "info")) { + capwap_logging_verboselevel(CAPWAP_LOGGING_INFO); + } else if (!strcmp(configString, "debug")) { + capwap_logging_verboselevel(CAPWAP_LOGGING_DEBUG); + } else { + capwap_logging_error("Invalid configuration file, unknown logging.level value"); + return 0; + } + } + + /* Logging output interface */ + configSetting = config_lookup(config, "logging.output"); + if (configSetting != NULL) { + int count = config_setting_length(configSetting); + + /* Disable output interface */ + capwap_logging_disable_allinterface(); + + /* Enable selected interface */ + for (i = 0; i < count; i++) { + config_setting_t* configElement = config_setting_get_elem(configSetting, i); + if ((configElement != NULL) && (config_setting_lookup_string(configElement, "mode", &configString) == CONFIG_TRUE)) { + if (!strcmp(configString, "stdout")) { + capwap_logging_enable_console(0); + } else if (!strcmp(configString, "stderr")) { + capwap_logging_enable_console(1); + } else { + capwap_logging_error("Invalid configuration file, unknown logging.output value"); + return 0; + } + } + } + } + } + } + + /* Set name of WTP */ + if (config_lookup_string(config, "application.name", &configString) == CONFIG_TRUE) { + if (strlen(configString) > CAPWAP_WTPNAME_MAXLENGTH) { + capwap_logging_error("Invalid configuration file, application.name string length exceeded"); + return 0; + } + + strcpy(g_wtp.name.name, configString); + } + + /* Set location of WTP */ + if (config_lookup_string(config, "application.location", &configString) == CONFIG_TRUE) { + if (strlen(configString) > CAPWAP_LOCATION_MAXLENGTH) { + capwap_logging_error("Invalid configuration file, application.location string length exceeded"); + return 0; + } + + strcpy(g_wtp.location.value, configString); + } + + /* Set binding of WTP */ + if (config_lookup_string(config, "application.binding", &configString) == CONFIG_TRUE) { + if (!strcmp(configString, "802.11")) { + g_wtp.binding = CAPWAP_WIRELESS_BINDING_IEEE80211; + } else if (!strcmp(configString, "EPCGlobal")) { + g_wtp.binding = CAPWAP_WIRELESS_BINDING_EPCGLOBAL; + } else { + capwap_logging_error("Invalid configuration file, unknown application.binding value"); + return 0; + } + } + + /* Set tunnelmode of WTP */ + if (config_lookup(config, "application.tunnelmode") != NULL) { + g_wtp.mactunnel.mode = 0; + if (config_lookup_bool(config, "application.tunnelmode.nativeframe", &configInt) == CONFIG_TRUE) { + if (configInt != 0) { + g_wtp.mactunnel.mode |= CAPWAP_WTP_NATIVE_FRAME_TUNNEL; + } + } + + if (config_lookup_bool(config, "application.tunnelmode.ethframe", &configInt) == CONFIG_TRUE) { + if (configInt != 0) { + g_wtp.mactunnel.mode |= CAPWAP_WTP_8023_FRAME_TUNNEL; + } + } + + if (config_lookup_bool(config, "application.tunnelmode.localbridging", &configInt) == CONFIG_TRUE) { + if (configInt != 0) { + g_wtp.mactunnel.mode |= CAPWAP_WTP_LOCAL_BRIDGING; + } + } + } + + /* Set mactype of WTP */ + if (config_lookup_string(config, "application.mactype", &configString) == CONFIG_TRUE) { + if (!strcmp(configString, "localmac")) { + g_wtp.mactype.type = CAPWAP_LOCALMAC; + } else if (!strcmp(configString, "splitmac")) { + g_wtp.mactype.type = CAPWAP_SPLITMAC; + } else { + capwap_logging_error("Invalid configuration file, unknown application.mactype value"); + return 0; + } + } + + /* Set VendorID Boardinfo of WTP */ + if (config_lookup_int(config, "application.boardinfo.idvendor", &configLongInt) == CONFIG_TRUE) { + g_wtp.boarddata.vendor = (unsigned long)configLongInt; + } + + /* Set Element Boardinfo of WTP */ + configSetting = config_lookup(config, "application.boardinfo.element"); + if (configSetting != NULL) { + int count = config_setting_length(configSetting); + + for (i = 0; i < count; i++) { + config_setting_t* configElement = config_setting_get_elem(configSetting, i); + if (configElement != NULL) { + const char* configName; + if (config_setting_lookup_string(configElement, "name", &configName) == CONFIG_TRUE) { + const char* configValue; + if (config_setting_lookup_string(configElement, "value", &configValue) == CONFIG_TRUE) { + int lengthValue = strlen(configValue); + if (lengthValue < CAPWAP_BOARD_SUBELEMENT_MAXDATA) { + struct capwap_wtpboarddata_board_subelement* element = (struct capwap_wtpboarddata_board_subelement*)capwap_array_get_item_pointer(g_wtp.boarddata.boardsubelement, g_wtp.boarddata.boardsubelement->count); + + if (!strcmp(configName, "model")) { + element->type = CAPWAP_BOARD_SUBELEMENT_MODELNUMBER; + element->length = lengthValue; + strcpy(element->data, configValue); + } else if (!strcmp(configName, "serial")) { + element->type = CAPWAP_BOARD_SUBELEMENT_SERIALNUMBER; + element->length = lengthValue; + strcpy(element->data, configValue); + } else if (!strcmp(configName, "id")) { + element->type = CAPWAP_BOARD_SUBELEMENT_ID; + element->length = lengthValue; + strcpy(element->data, configValue); + } else if (!strcmp(configName, "revision")) { + element->type = CAPWAP_BOARD_SUBELEMENT_REVISION; + element->length = lengthValue; + strcpy(element->data, configValue); + } else if (!strcmp(configName, "macaddress")) { + const char* configType; + if (config_setting_lookup_string(configElement, "type", &configType) == CONFIG_TRUE) { + if (!strcmp(configType, "interface")) { + element->type = CAPWAP_BOARD_SUBELEMENT_MACADDRESS; + element->length = capwap_get_macaddress_from_interface(configValue, element->data); + if (!element->length) { + capwap_logging_error("Invalid configuration file, unable found macaddress of interface: '%s'", configValue); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, unknown application.boardinfo.element.type value"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, element application.boardinfo.element.type not found"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, unknown application.boardinfo.element.name value"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, application.boardinfo.element.value string length exceeded"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, element application.boardinfo.element.value not found"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, element application.boardinfo.element.name not found"); + return 0; + } + } + } + } + + /* Set Radio descriptor of WTP */ + configSetting = config_lookup(config, "application.descriptor.radio"); + if (configSetting != NULL) { + int count = config_setting_length(configSetting); + + if (g_wtp.binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + for (i = 0; i < count; i++) { + struct wtp_radio* radio; + char radioname[IFNAMSIZ]; + unsigned char radiotype = 0; + int radiostatus = WTP_RADIO_ENABLED; + + config_setting_t* configElement = config_setting_get_elem(configSetting, i); + if (configElement != NULL) { + if (config_setting_lookup_string(configElement, "device", &configString) == CONFIG_TRUE) { + if (*configString && (strlen(configString) < IFNAMSIZ)) { + strcpy(radioname, configString); + if (config_setting_lookup_string(configElement, "type", &configString) == CONFIG_TRUE) { + int length = strlen(configString); + + for (i = 0; i < length; i++) { + switch (configString[i]) { + case 'a': { + radiotype |= CAPWAP_RADIO_TYPE_80211A; + break; + } + + case 'b': { + radiotype |= CAPWAP_RADIO_TYPE_80211B; + break; + } + + case 'g': { + radiotype |= CAPWAP_RADIO_TYPE_80211G; + break; + } + + case 'n': { + radiotype |= CAPWAP_RADIO_TYPE_80211N; + break; + } + + default: { + capwap_logging_error("Invalid configuration file, unknown application.descriptor.radio.type value"); + return 0; + } + } + } + + if (radiotype != 0) { + if (config_setting_lookup_string(configElement, "status", &configString) == CONFIG_TRUE) { + if (!strcmp(configString, "enabled")) { + radiostatus = WTP_RADIO_ENABLED; + } else if (!strcmp(configString, "disabled")) { + radiostatus = WTP_RADIO_DISABLED; + } else if (!strcmp(configString, "hwfailure")) { + radiostatus = WTP_RADIO_HWFAILURE; + } else if (!strcmp(configString, "swfailure")) { + radiostatus = WTP_RADIO_SWFAILURE; + } else { + capwap_logging_error("Invalid configuration file, unknown application.descriptor.radio.type value"); + return 0; + } + } + + /* Create new radio device */ + radio = (struct wtp_radio*)capwap_array_get_item_pointer(g_wtp.radios, g_wtp.radios->count); + strcpy(radio->device, radioname); + radio->radioinformation.radioid = g_wtp.radios->count; + radio->radioinformation.radiotype = radiotype; + radio->status = radiostatus; + } else { + capwap_logging_error("Invalid configuration file, unknown application.descriptor.radio.type value"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, element application.descriptor.radio.type not found"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, application.descriptor.radio.device string length exceeded"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, element application.descriptor.radio.device not found"); + return 0; + } + } + } + + /* Update radio status */ + g_wtp.descriptor.maxradios = g_wtp.radios->count; + g_wtp.descriptor.radiosinuse = wtp_update_radio_in_use(); + } + } + + /* Set encryption of WTP */ + configSetting = config_lookup(config, "application.descriptor.encryption"); + if (configSetting != NULL) { + unsigned short capability = 0; + int count = config_setting_length(configSetting); + struct capwap_wtpdescriptor_encrypt_subelement* encrypt; + + if (g_wtp.binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + for (i = 0; i < count; i++) { + const char* encryption = config_setting_get_string_elem(configSetting, i); + if (encryption != NULL) { + if (!strcmp(encryption, "802.11_AES")) { + capability |= 0; /* TODO */ + } else if (!strcmp(encryption, "802.11_TKIP")) { + capability |= 0; /* TODO */ + } else { + capwap_logging_error("Invalid configuration file, invalid application.descriptor.encryption value"); + return 0; + } + } + } + } + + /* */ + encrypt = (struct capwap_wtpdescriptor_encrypt_subelement*)capwap_array_get_item_pointer(g_wtp.descriptor.encryptsubelement, g_wtp.descriptor.encryptsubelement->count); + encrypt->wbid = g_wtp.binding; + encrypt->capabilities = capability; + } else { + capwap_logging_error("Invalid configuration file, application.descriptor.encryption not found"); + return 0; + } + + /* Set info descriptor of WTP */ + configSetting = config_lookup(config, "application.descriptor.info"); + if (configSetting != NULL) { + int count = config_setting_length(configSetting); + + for (i = 0; i < count; i++) { + config_setting_t* configElement = config_setting_get_elem(configSetting, i); + if (configElement != NULL) { + long int configVendor; + if (config_setting_lookup_int(configElement, "idvendor", &configVendor) == CONFIG_TRUE) { + const char* configType; + if (config_setting_lookup_string(configElement, "type", &configType) == CONFIG_TRUE) { + const char* configValue; + if (config_setting_lookup_string(configElement, "value", &configValue) == CONFIG_TRUE) { + int lengthValue = strlen(configValue); + if (lengthValue < CAPWAP_WTPDESC_SUBELEMENT_MAXDATA) { + unsigned short type; + struct capwap_wtpdescriptor_desc_subelement* desc; + + if (!strcmp(configType, "hardware")) { + type = CAPWAP_WTPDESC_SUBELEMENT_HARDWAREVERSION; + } else if (!strcmp(configType, "software")) { + type = CAPWAP_WTPDESC_SUBELEMENT_SOFTWAREVERSION; + } else if (!strcmp(configType, "boot")) { + type = CAPWAP_WTPDESC_SUBELEMENT_BOOTVERSION; + } else if (!strcmp(configType, "other")) { + type = CAPWAP_WTPDESC_SUBELEMENT_OTHERVERSION; + } else { + capwap_logging_error("Invalid configuration file, unknown application.descriptor.info.type value"); + return 0; + } + + desc = (struct capwap_wtpdescriptor_desc_subelement*)capwap_array_get_item_pointer(g_wtp.descriptor.descsubelement, g_wtp.descriptor.descsubelement->count); + desc->vendor = (unsigned long)configVendor; + desc->type = type; + desc->length = lengthValue; + strcpy(desc->data, configValue); + } else { + capwap_logging_error("Invalid configuration file, application.descriptor.info.value string length exceeded"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, element application.descriptor.info.value not found"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, element application.descriptor.info.type not found"); + return 0; + } + } else { + capwap_logging_error("Invalid configuration file, element application.descriptor.info.idvendor not found"); + return 0; + } + } + } + } + + /* Set ECN of WTP */ + if (config_lookup_string(config, "application.ecn", &configString) == CONFIG_TRUE) { + if (!strcmp(configString, "full")) { + g_wtp.ecn.flag = CAPWAP_FULL_ECN_SUPPORT; + } else if (!strcmp(configString, "limited")) { + g_wtp.ecn.flag = CAPWAP_LIMITED_ECN_SUPPORT; + } else { + capwap_logging_error("Invalid configuration file, unknown application.ecn value"); + return 0; + } + } + + /* Set Timer of WTP */ + if (config_lookup_int(config, "application.timer.statistics", &configLongInt) == CONFIG_TRUE) { + if ((configLongInt > 0) && (configLongInt < 65536)) { + g_wtp.statisticstimer.timer = (unsigned short)configLongInt; + } else { + capwap_logging_error("Invalid configuration file, invalid application.timer.statistics value"); + return 0; + } + } + + /* Set DTLS of WTP */ + if (config_lookup_bool(config, "application.dtls.enable", &configInt) == CONFIG_TRUE) { + if (configInt != 0) { + struct capwap_dtls_param dtlsparam; + + /* Init dtls param */ + memset(&dtlsparam, 0, sizeof(struct capwap_dtls_param)); + dtlsparam.type = CAPWAP_DTLS_CLIENT; + + /* Set DTLS Policy of WTP */ + if (config_lookup(config, "application.dtls.dtlspolicy") != NULL) { + g_wtp.validdtlsdatapolicy = 0; + if (config_lookup_bool(config, "application.dtls.dtlspolicy.cleardatachannel", &configInt) == CONFIG_TRUE) { + if (configInt != 0) { + g_wtp.validdtlsdatapolicy |= CAPWAP_ACDESC_CLEAR_DATA_CHANNEL_ENABLED; + } + } + + if (config_lookup_bool(config, "application.dtls.dtlspolicy.dtlsdatachannel", &configInt) == CONFIG_TRUE) { + if (configInt != 0) { + g_wtp.validdtlsdatapolicy |= CAPWAP_ACDESC_DTLS_DATA_CHANNEL_ENABLED; + } + } + } + + /* Set DTLS type of WTP */ + if (config_lookup_string(config, "application.dtls.type", &configString) == CONFIG_TRUE) { + if (!strcmp(configString, "x509")) { + dtlsparam.mode = CAPWAP_DTLS_MODE_CERTIFICATE; + } else if (!strcmp(configString, "presharedkey")) { + dtlsparam.mode = CAPWAP_DTLS_MODE_PRESHAREDKEY; + } else { + capwap_logging_error("Invalid configuration file, unknown application.dtls.type value"); + return 0; + } + } + + /* Set DTLS configuration of WTP */ + if (dtlsparam.mode == CAPWAP_DTLS_MODE_CERTIFICATE) { + if (config_lookup_string(config, "application.dtls.x509.calist", &configString) == CONFIG_TRUE) { + if (strlen(configString) > 0) { + dtlsparam.cert.fileca = capwap_duplicate_string(configString); + } + } + + if (config_lookup_string(config, "application.dtls.x509.certificate", &configString) == CONFIG_TRUE) { + if (strlen(configString) > 0) { + dtlsparam.cert.filecert = capwap_duplicate_string(configString); + } + } + + if (config_lookup_string(config, "application.dtls.x509.privatekey", &configString) == CONFIG_TRUE) { + if (strlen(configString) > 0) { + dtlsparam.cert.filekey = capwap_duplicate_string(configString); + } + } + + if (config_lookup_string(config, "application.dtls.x509.privatekeypassword", &configString) == CONFIG_TRUE) { + if (strlen(configString) > 0) { + dtlsparam.cert.pwdprivatekey = capwap_duplicate_string(configString); + } + } + + if (dtlsparam.cert.fileca && dtlsparam.cert.filecert && dtlsparam.cert.filekey) { + if (capwap_crypt_createcontext(&g_wtp.dtlscontext, &dtlsparam)) { + g_wtp.enabledtls = 1; + } + } + + /* Free dtls param */ + if (dtlsparam.cert.fileca) { + capwap_free(dtlsparam.cert.fileca); + } + + if (dtlsparam.cert.filecert) { + capwap_free(dtlsparam.cert.filecert); + } + + if (dtlsparam.cert.filekey) { + capwap_free(dtlsparam.cert.filekey); + } + + if (dtlsparam.cert.pwdprivatekey) { + capwap_free(dtlsparam.cert.pwdprivatekey); + } + + if (!g_wtp.enabledtls) { + return 0; + } + } else if (dtlsparam.mode == CAPWAP_DTLS_MODE_PRESHAREDKEY) { + /* TODO */ + } + } + } + + /* Set interface binding of WTP */ + if (config_lookup_string(config, "application.network.binding", &configString) == CONFIG_TRUE) { + if (strlen(configString) > (IFNAMSIZ - 1)) { + capwap_logging_error("Invalid configuration file, application.network.binding string length exceeded"); + return 0; + } + + strcpy(g_wtp.net.bind_interface, configString); + } + + /* Set mtu of WTP */ + if (config_lookup_int(config, "application.network.mtu", &configLongInt) == CONFIG_TRUE) { + if ((configLongInt > 0) && (configLongInt < 65536)) { + g_wtp.mtu = (unsigned short)configLongInt; + } else { + capwap_logging_error("Invalid configuration file, invalid application.network.mtu value"); + return 0; + } + } + + /* Set network port of WTP */ + if (config_lookup_int(config, "application.network.port", &configLongInt) == CONFIG_TRUE) { + if ((configLongInt > 0) && (configLongInt < 65536)) { + g_wtp.net.bind_sock_ctrl_port = (unsigned short)configLongInt; + } else { + capwap_logging_error("Invalid configuration file, invalid application.network.port value"); + return 0; + } + } + + /* Set transport of WTP */ + if (config_lookup_string(config, "application.network.transport", &configString) == CONFIG_TRUE) { + if (!strcmp(configString, "udp")) { + g_wtp.transport.type = CAPWAP_UDP_TRANSPORT; + } else if (!strcmp(configString, "udplite")) { + g_wtp.transport.type = CAPWAP_UDPLITE_TRANSPORT; + } else { + capwap_logging_error("Invalid configuration file, unknown application.network.transport value"); + return 0; + } + } + + /* Set ipv4 & ipv6 of WTP */ + if (config_lookup_bool(config, "application.network.ipv4", &configIPv4) != CONFIG_TRUE) { + configIPv4 = 1; + } + + if (config_lookup_bool(config, "application.network.ipv6", &configIPv6) != CONFIG_TRUE) { + configIPv6 = 1; + } + + if (configIPv4 && configIPv6) { + g_wtp.net.sock_family = AF_UNSPEC; + } else if (!configIPv4 && !configIPv6) { + capwap_logging_error("Invalid configuration file, request enable application.network.ipv4 or application.network.ipv6"); + return 0; + } else { + g_wtp.net.sock_family = (configIPv4 ? AF_INET : AF_INET6); + } + + /* Set ip dual stack of WTP */ + if (config_lookup_bool(config, "application.network.ipdualstack", &configInt) == CONFIG_TRUE) { + if (!configInt) { + g_wtp.net.bind_ctrl_flags |= CAPWAP_IPV6ONLY_FLAG; + g_wtp.net.bind_data_flags |= CAPWAP_IPV6ONLY_FLAG; + } else { + g_wtp.net.bind_ctrl_flags &= ~CAPWAP_IPV6ONLY_FLAG; + g_wtp.net.bind_data_flags &= ~CAPWAP_IPV6ONLY_FLAG; + } + } + + /* Set search discovery of WTP */ + if (config_lookup_bool(config, "application.acdiscovery.search", &configInt) == CONFIG_TRUE) { + g_wtp.acdiscoveryrequest = (configInt ? 1 : 0); + } + + /* Set discovery host of WTP */ + configSetting = config_lookup(config, "application.acdiscovery.host"); + if (configSetting != NULL) { + int count = config_setting_length(configSetting); + + for (i = 0; i < count; i++) { + const char* address = config_setting_get_string_elem(configSetting, i); + if (address != NULL) { + struct sockaddr_storage acaddr; + + /* Parsing address */ + if (capwap_address_from_string(address, &acaddr)) { + if (!CAPWAP_GET_NETWORK_PORT(&acaddr)) { + CAPWAP_SET_NETWORK_PORT(&acaddr, CAPWAP_CONTROL_PORT); + } + + wtp_add_acaddress(&acaddr, g_wtp.acdiscoveryarray); + g_wtp.discoverytype.type = CAPWAP_ELEMENT_DISCOVERYTYPE_TYPE_STATIC; + } else { + capwap_logging_error("Invalid configuration file, invalid application.acdiscovery.host value"); + return 0; + } + } + } + } + + /* Set preferred ac of WTP */ + configSetting = config_lookup(config, "application.acprefered.host"); + if (configSetting != NULL) { + int count = config_setting_length(configSetting); + + for (i = 0; i < count; i++) { + const char* address = config_setting_get_string_elem(configSetting, i); + if (address != NULL) { + struct sockaddr_storage acaddr; + + /* Parsing address */ + if (capwap_address_from_string(address, &acaddr)) { + if (!CAPWAP_GET_NETWORK_PORT(&acaddr)) { + CAPWAP_SET_NETWORK_PORT(&acaddr, CAPWAP_CONTROL_PORT); + } + + wtp_add_acaddress(&acaddr, g_wtp.acpreferedarray); + } else { + capwap_logging_error("Invalid configuration file, invalid application.acprefered.host value"); + return 0; + } + } + } + } + + return 1; +} + +/* Parsing configuration */ +static int wtp_parsing_configuration(config_t* config) { + const char* configString; + + if (config_lookup_string(config, "version", &configString) == CONFIG_TRUE) { + if (strcmp(configString, "1.0") == 0) { + return wtp_parsing_configuration_1_0(config); + } + + capwap_logging_error("Invalid configuration file, '%s' is not supported", configString); + } else { + capwap_logging_error("Invalid configuration file, unable to found version tag"); + } + + return 0; +} + +/* Load configuration */ +static int wtp_load_configuration(int argc, char **argv) { + int c; + int result = 0; + config_t config; + + ASSERT(argc >= 0); + ASSERT(argv != NULL); + + /* Parsing command line */ + opterr = 0; + while ((c = getopt(argc, argv, "hc:")) != -1) { + switch (c) { + case 'h': { + wtp_print_usage(); + return 0; + } + + case 'c': { + if (strlen(optarg) < sizeof(g_configurationfile)) { + strcpy(g_configurationfile, optarg); + } else { + capwap_logging_error("Invalid -%c argument", optopt); + return -1; + } + + break; + } + + case '?': { + if (optopt == 'c') { + capwap_logging_error("Option -%c requires an argument", optopt); + } else { + capwap_logging_error("Unknown option character `\\x%x'", optopt); + } + + wtp_print_usage(); + return -1; + } + } + } + + /* Init libconfig */ + config_init(&config); + + /* Load configuration */ + if (config_read_file(&config, g_configurationfile) == CONFIG_TRUE) { + result = wtp_parsing_configuration(&config); + } else { + result = -1; + capwap_logging_error("Unable load the configuration file '%s': %s (%d)", g_configurationfile, config_error_text(&config), config_error_line(&config)); + } + + /* Free libconfig */ + config_destroy(&config); + return result; +} + +/* Init WTP */ +static int wtp_configure(void) { + /* If request add default acdiscovery */ + if (!g_wtp.acdiscoveryarray->count) { + if (!wtp_add_default_acaddress()) { + capwap_logging_debug("Unable add default AC discovery"); + return WTP_ERROR_NETWORK; + } + } + + /* Bind to any address */ + if (!capwap_bind_sockets(&g_wtp.net)) { + capwap_logging_fatal("Cannot bind address"); + return WTP_ERROR_NETWORK; + } + + return CAPWAP_SUCCESSFUL; +} + +/* */ +int wtp_update_radio_in_use() { + /* TODO */ + return g_wtp.radios->count; +} + +/* Main*/ +int main(int argc, char** argv) { + int value; + int result = CAPWAP_SUCCESSFUL; + + /* Init logging */ + capwap_logging_init(); + capwap_logging_verboselevel(CAPWAP_LOGGING_ERROR); + capwap_logging_enable_console(1); + + /* Init capwap */ + if (geteuid() != 0) { + capwap_logging_fatal("Request root privileges"); + return CAPWAP_REQUEST_ROOT; + } + + /* Init random generator */ + capwap_init_rand(); + + /* Init crypt */ + if (!capwap_crypt_init()) { + capwap_logging_fatal("Error to init crypt engine"); + return CAPWAP_CRYPT_ERROR; + } + + /* Alloc a WTP */ + if (!wtp_init()) { + capwap_logging_fatal("Error to init WTP engine"); + return WTP_ERROR_SYSTEM_FAILER; + } + + /* Read configuration file */ + value = wtp_load_configuration(argc, argv); + if (value < 0) { + result = WTP_ERROR_LOAD_CONFIGURATION; + } else if (value > 0) { + capwap_logging_info("Startup WTP"); + + /* Start WTP */ + wtp_dfa_change_state(CAPWAP_START_TO_IDLE_STATE); + + /* Complete configuration WTP */ + result = wtp_configure(); + if (result == CAPWAP_SUCCESSFUL) { + /* Init complete */ + wtp_dfa_change_state(CAPWAP_IDLE_STATE); + + /* Running WTP */ + result = wtp_dfa_execute(); + + /* Close socket */ + capwap_close_sockets(&g_wtp.net); + } + + capwap_logging_info("Terminate WTP"); + } + + /* Free memory */ + wtp_destroy(); + + /* Free crypt */ + capwap_crypt_free(); + + /* Check memory leak */ + if (capwap_check_memory_leak(1)) { + if (result == CAPWAP_SUCCESSFUL) + result = WTP_ERROR_MEMORY_LEAK; + } + + /* Close logging */ + capwap_logging_close(); + + return result; +} diff --git a/src/wtp/wtp.h b/src/wtp/wtp.h new file mode 100644 index 0000000..a180ce5 --- /dev/null +++ b/src/wtp/wtp.h @@ -0,0 +1,147 @@ +#ifndef __CAPWAP_WTP_HEADER__ +#define __CAPWAP_WTP_HEADER__ + +/* standard include */ +#include "capwap.h" +#include "capwap_dtls.h" +#include "capwap_network.h" +#include "capwap_protocol.h" + +/* WTP Configuration */ +#define WTP_STANDARD_CONFIGURATION_FILE "/etc/capwap/wtp.conf" + +/* WTP runtime error return code */ +#define WTP_ERROR_SYSTEM_FAILER -1000 +#define WTP_ERROR_LOAD_CONFIGURATION -1001 +#define WTP_ERROR_NETWORK -1002 +#define WTP_ERROR_MEMORY_LEAK 1 + +/* Min and max dfa values */ +#define WTP_MIN_DISCOVERY_INTERVAL 2 +#define WTP_DEFAULT_DISCOVERY_INTERVAL 20 +#define WTP_MAX_DISCOVERY_INTERVAL 180 +#define WTP_DEFAULT_DISCOVERY_COUNT 10 +#define WTP_DEFAULT_SILENT_INTERVAL 30 +#define WTP_DEFAULT_RETRANSMIT_INTERVAL 3 +#define WTP_MAX_RETRANSMIT 5 +#define WTP_MIN_WAITDTLS_INTERVAL 30 +#define WTP_DEFAULT_WAITDTLS_INTERVAL 60 +#define WTP_DEFAULT_STATISTICSTIMER_INTERVAL 120 +#define WTP_DEFAULT_DATACHANNEL_KEEPALIVE 30 +#define WTP_DEFAULT_DATACHANNEL_KEEPALIVEDEAD 60 +#define WTP_MAX_DATACHANNEL_KEEPALIVEDEAD 240 +#define WTP_DEFAULT_ECHO_INTERVAL 30 +#define WTP_DEFAULT_DTLS_SESSION_DELETE 5 +#define WTP_DEFAULT_FAILED_DTLS_SESSION_RETRY 3 + +/* WTP State machine */ +struct wtp_state { + unsigned long state; + + /* Discovery Information */ + int rfcDiscoveryInterval; + int rfcMaxDiscoveryInterval; + int rfcDiscoveryCount; + int rfcMaxDiscoveries; + + /* Sulking Information */ + int rfcSilentInterval; + + /* Run */ + int rfcEchoInterval; + + /* Dtls Information */ + int rfcFailedDTLSSessionCount; + int rfcFailedDTLSAuthFailCount; + int rfcMaxFailedDTLSSessionRetry; + + /* Request retransmit */ + int rfcRetransmitInterval; + int rfcRetransmitCount; + int rfcMaxRetransmit; + + /* Data channel */ + int rfcDataChannelKeepAlive; + int rfcDataChannelDeadInterval; + + /* Dtls */ + int rfcWaitDTLS; + int rfcDTLSSessionDelete; +}; + +/* WTP */ +struct wtp_t { + int running; + + struct wtp_state dfa; + struct capwap_network net; + + struct capwap_wtpname_element name; + struct capwap_acname_element acname; + struct capwap_location_element location; + + unsigned short binding; + + struct capwap_discoverytype_element discoverytype; + struct capwap_wtpframetunnelmode_element mactunnel; + struct capwap_wtpmactype_element mactype; + struct capwap_wtpboarddata_element boarddata; + struct capwap_wtpdescriptor_element descriptor; + + struct capwap_sessionid_element sessionid; + + struct capwap_ecnsupport_element ecn; + struct capwap_transport_element transport; + struct capwap_statisticstimer_element statisticstimer; + struct capwap_wtprebootstat_element rebootstat; + + unsigned char localseqnumber; + unsigned char remoteseqnumber; + unsigned short mtu; + unsigned short fragmentid; + capwap_fragment_packet_array* requestfragmentpacket; + capwap_fragment_packet_array* responsefragmentpacket; + unsigned char lastrecvpackethash[16]; + + /* */ + int acdiscoveryrequest; + unsigned long acpreferedselected; + struct capwap_array* acdiscoveryarray; + struct capwap_array* acpreferedarray; + struct capwap_array* acdiscoveryresponse; + + struct sockaddr_storage wtpctrladdress; + struct sockaddr_storage wtpdataaddress; + struct sockaddr_storage acctrladdress; + struct sockaddr_storage acdataaddress; + struct capwap_socket acctrlsock; + struct capwap_socket acdatasock; + + struct capwap_array* radios; + + /* Dtls */ + int enabledtls; + unsigned char dtlsdatapolicy; + unsigned char validdtlsdatapolicy; + struct capwap_dtls_context dtlscontext; + struct capwap_dtls ctrldtls; + struct capwap_dtls datadtls; +}; + +#define WTP_RADIO_ENABLED 0 +#define WTP_RADIO_DISABLED 1 +#define WTP_RADIO_HWFAILURE 2 +#define WTP_RADIO_SWFAILURE 3 + +struct wtp_radio { + char device[IFNAMSIZ]; + struct capwap_80211_wtpradioinformation_element radioinformation; + int status; +}; + +extern struct wtp_t g_wtp; + +/* */ +int wtp_update_radio_in_use(); + +#endif /* __CAPWAP_WTP_HEADER__ */ diff --git a/src/wtp/wtp_dfa.c b/src/wtp/wtp_dfa.c new file mode 100644 index 0000000..bd33fc1 --- /dev/null +++ b/src/wtp/wtp_dfa.c @@ -0,0 +1,459 @@ +#include "wtp.h" +#include "wtp_dfa.h" +#include "capwap_array.h" +#include "capwap_dfa.h" +#include "capwap_dtls.h" + +#include + +/* Handler signal */ +static void wtp_signal_handler(int signum) { + if ((signum == SIGINT) || (signum == SIGTERM)) { + g_wtp.running = 0; + } +} + +/* WTP state machine */ +int wtp_dfa_execute(void) { + int result = CAPWAP_SUCCESSFUL; + int action = WTP_DFA_NO_PACKET; + struct timeout_control timeout; + + struct capwap_packet packet; + capwap_fragment_list* defraglist; + char buffer[CAPWAP_MAX_PACKET_SIZE]; + int buffersize; + + int index; + struct sockaddr_storage recvfromaddr; + struct sockaddr_storage recvtoaddr; + int isrecvpacket = 0; + + struct pollfd* fds; + int fdscount; + + /* Init */ + capwap_init_timeout(&timeout); + capwap_set_timeout(0, &timeout, CAPWAP_TIMER_CONTROL_CONNECTION); /* Start DFA with timeout */ + + memset(&packet, 0, sizeof(struct capwap_packet)); + defraglist = capwap_defragment_init_list(); + + /* Configure poll struct */ + fdscount = CAPWAP_MAX_SOCKETS * 2; + fds = (struct pollfd*)capwap_alloc(sizeof(struct pollfd) * fdscount); + if (!fds) { + capwap_outofmemory(); + } + + /* Retrive all socket for polling */ + fdscount = capwap_network_set_pollfd(&g_wtp.net, fds, fdscount); + ASSERT(fdscount > 0); + + /* Handler signal */ + g_wtp.running = 1; + signal(SIGINT, wtp_signal_handler); + signal(SIGTERM, wtp_signal_handler); + + for (;;) { + /* If request wait packet from AC */ + isrecvpacket = 0; + if ((action == WTP_DFA_ACCEPT_PACKET) || (action == WTP_DFA_DROP_PACKET)) { + buffersize = CAPWAP_MAX_PACKET_SIZE; + index = capwap_recvfrom(fds, fdscount, buffer, &buffersize, &recvfromaddr, &recvtoaddr, &timeout); + if (!g_wtp.running) { + break; + } + + if (index >= 0) { + if (action == WTP_DFA_DROP_PACKET) { + /* Drop packet */ + continue; + } else { + int check; + + /* Check of packet */ + capwap_get_network_socket(&g_wtp.net, &packet.socket, fds[index].fd); + check = capwap_sanity_check(packet.socket.isctrlsocket, g_wtp.dfa.state, buffer, buffersize, g_wtp.ctrldtls.enable, g_wtp.datadtls.enable); + if (check == CAPWAP_DTLS_PACKET) { + struct capwap_dtls* dtls = (packet.socket.isctrlsocket ? &g_wtp.ctrldtls : &g_wtp.datadtls); + + if (dtls->enable) { + int oldaction = dtls->action; + + /* Decrypt packet */ + buffersize = capwap_decrypt_packet(dtls, buffer, buffersize, NULL, CAPWAP_MAX_PACKET_SIZE); + if (buffersize > 0) { + check = CAPWAP_PLAIN_PACKET; + } else if (buffersize == CAPWAP_ERROR_AGAIN) { + /* Check is handshake complete */ + if ((oldaction == CAPWAP_DTLS_ACTION_HANDSHAKE) && (dtls->action == CAPWAP_DTLS_ACTION_DATA)) { + if (packet.socket.isctrlsocket) { + if (g_wtp.dfa.state == CAPWAP_DTLS_CONNECT_STATE) { + check = CAPWAP_NONE_PACKET; + wtp_dfa_change_state(CAPWAP_DTLS_CONNECT_TO_JOIN_STATE); + action = WTP_DFA_NO_PACKET; + } else { + /* TODO */ /* Connection error */ + check = CAPWAP_WRONG_PACKET; + } + } else { + if (g_wtp.dfa.state == CAPWAP_DATA_CHECK_TO_RUN_STATE) { + check = CAPWAP_NONE_PACKET; + action = WTP_DFA_NO_PACKET; + } else { + /* TODO */ /* Connection error */ + check = CAPWAP_WRONG_PACKET; + } + } + } + + continue; /* Next packet */ + } else { + if ((oldaction == CAPWAP_DTLS_ACTION_DATA) && (dtls->action == CAPWAP_DTLS_ACTION_SHUTDOWN)) { + action = wtp_teardown_connection(&timeout); + } + + continue; /* Next packet */ + } + } else { + continue; /* Drop packet */ + } + } else if (check == CAPWAP_WRONG_PACKET) { + capwap_logging_debug("Warning: sanity check failure"); + /* Drop packet */ + continue; + } + + /* */ + if (check == CAPWAP_PLAIN_PACKET) { + /* If request, defragmentation packet */ + check = capwap_defragment_packets(&recvfromaddr, buffer, buffersize, defraglist, &packet); + if (check == CAPWAP_REQUEST_MORE_FRAGMENT) { + continue; + } else if (check != CAPWAP_RECEIVE_COMPLETE_PACKET) { + /* Discard fragments */ + capwap_defragment_remove_sender(defraglist, &recvfromaddr); + continue; + } + + /* Detect local address */ + if (recvtoaddr.ss_family == AF_UNSPEC) { + if (capwap_get_localaddress_by_remoteaddress(&recvtoaddr, &recvfromaddr, g_wtp.net.bind_interface, (!(g_wtp.net.bind_ctrl_flags & CAPWAP_IPV6ONLY_FLAG) ? 1 : 0))) { + struct sockaddr_storage sockinfo; + socklen_t sockinfolen = sizeof(struct sockaddr_storage); + + memset(&sockinfo, 0, sizeof(struct sockaddr_storage)); + if (getsockname(fds[index].fd, (struct sockaddr*)&sockinfo, &sockinfolen) < 0) { + break; + } + + CAPWAP_SET_NETWORK_PORT(&recvtoaddr, CAPWAP_GET_NETWORK_PORT(&sockinfo)); + } + } + + /* Receive a complete packet */ + isrecvpacket = 1; + memcpy(&packet.localaddr, &recvtoaddr, sizeof(struct sockaddr_storage)); + memcpy(&packet.remoteaddr, &recvfromaddr, sizeof(struct sockaddr_storage)); + + /* Check for already response to packet */ + if (packet.socket.isctrlsocket) { + if (capwap_recv_retrasmitted_request(&g_wtp.ctrldtls, &packet, g_wtp.remoteseqnumber, g_wtp.lastrecvpackethash, &g_wtp.acctrlsock, g_wtp.responsefragmentpacket, &g_wtp.wtpctrladdress, &g_wtp.acctrladdress)) { + capwap_free_packet(&packet); + continue; + } + + /* Check message type */ + if (!capwap_check_message_type(&g_wtp.ctrldtls, &packet, g_wtp.mtu)) { + capwap_free_packet(&packet); + continue; + } + } + } + } + } else if (index == CAPWAP_RECV_ERROR_INTR) { + /* Ignore recv */ + continue; + } else if (index == CAPWAP_RECV_ERROR_SOCKET) { + /* Socket close */ + break; + } + } + + /* Execute state */ + switch (g_wtp.dfa.state) { + case CAPWAP_IDLE_STATE: { + action = wtp_dfa_state_idle((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_IDLE_TO_DISCOVERY_STATE: { + action = wtp_dfa_state_idle_to_discovery((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_IDLE_TO_DTLS_SETUP_STATE: { + action = wtp_dfa_state_idle_to_dtlssetup((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_DISCOVERY_STATE: { + action = wtp_dfa_state_discovery((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_DISCOVERY_TO_SULKING_STATE: { + action = wtp_dfa_state_discovery_to_sulking((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_DISCOVERY_TO_DTLS_SETUP_STATE: { + action = wtp_dfa_state_discovery_to_dtlssetup((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_SULKING_STATE: { + action = wtp_dfa_state_sulking((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_SULKING_TO_IDLE_STATE: { + action = wtp_dfa_state_sulking_to_idle((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_DTLS_SETUP_STATE: { + action = wtp_dfa_state_dtlssetup((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_DTLS_SETUP_TO_IDLE_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DTLS_SETUP_TO_SULKING_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DTLS_SETUP_TO_AUTHORIZE_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_AUTHORIZE_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_AUTHORIZE_TO_DTLS_SETUP_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_AUTHORIZE_TO_DTLS_CONNECT_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_AUTHORIZE_TO_DTLS_TEARDOWN_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DTLS_CONNECT_STATE: { + action = wtp_dfa_state_dtlsconnect((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_DTLS_CONNECT_TO_DTLS_TEARDOWN_STATE: { + action = wtp_dfa_state_dtlsconnect_to_dtlsteardown((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_DTLS_CONNECT_TO_JOIN_STATE: { + action = wtp_dfa_state_dtlsconnect_to_join((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_DTLS_TEARDOWN_STATE: { + action = wtp_dfa_state_dtlsteardown((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_DTLS_TEARDOWN_TO_IDLE_STATE: { + action = wtp_dfa_state_dtlsteardown_to_idle((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_DTLS_TEARDOWN_TO_SULKING_STATE: { + action = wtp_dfa_state_dtlsteardown_to_sulking((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_JOIN_STATE: { + action = wtp_dfa_state_join((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE: { + action = wtp_dfa_state_join_to_dtlsteardown((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_JOIN_TO_IMAGE_DATA_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_JOIN_TO_CONFIGURE_STATE: { + action = wtp_dfa_state_join_to_configure((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_IMAGE_DATA_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_IMAGE_DATA_TO_RESET_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_IMAGE_DATA_TO_DTLS_TEARDOWN_STATE: { + action = wtp_dfa_state_imagedata_to_dtlsteardown((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_CONFIGURE_STATE: { + action = wtp_dfa_state_configure((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_CONFIGURE_TO_RESET_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_CONFIGURE_TO_DTLS_TEARDOWN_STATE: { + action = wtp_dfa_state_configure_to_dtlsteardown((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_CONFIGURE_TO_DATA_CHECK_STATE: { + action = wtp_dfa_state_configure_to_datacheck((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_RESET_STATE: { + action = wtp_dfa_state_reset((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_RESET_TO_DTLS_TEARDOWN_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DATA_CHECK_STATE: { + action = wtp_dfa_state_datacheck((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE: { + action = wtp_dfa_state_datacheck_to_dtlsteardown((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_DATA_CHECK_TO_RUN_STATE: { + action = wtp_dfa_state_datacheck_to_run((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_RUN_STATE: { + action = wtp_dfa_state_run((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE: { + action = wtp_dfa_state_run_to_dtlsteardown((isrecvpacket ? &packet : NULL), &timeout); + break; + } + + case CAPWAP_RUN_TO_RESET_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + case CAPWAP_DEAD_STATE: { + /* Never called with this state */ + ASSERT(0); + break; + } + + default: { + capwap_logging_debug("Unknown action event: %lu", g_wtp.dfa.state); + break; + } + } + + /* Free memory */ + if (isrecvpacket) { + capwap_free_packet(&packet); + } else { + capwap_defragment_flush_list(defraglist); + } + } + + /* Free DTSL Control */ + if (g_wtp.ctrldtls.enable) { + capwap_crypt_close(&g_wtp.ctrldtls); + capwap_crypt_freesession(&g_wtp.ctrldtls); + } + + /* Free DTLS Data */ + if (g_wtp.datadtls.enable) { + capwap_crypt_close(&g_wtp.datadtls); + capwap_crypt_freesession(&g_wtp.datadtls); + } + + /* Free memory */ + capwap_defragment_free_list(defraglist); + capwap_free(fds); + return result; +} + +/* Change WTP state machine */ +void wtp_dfa_change_state(int state) { + if (state != g_wtp.dfa.state) { + capwap_logging_debug("WTP change state from %s to %s", capwap_dfa_getname(g_wtp.dfa.state), capwap_dfa_getname(state)); + g_wtp.dfa.state = state; + } +} + +/* */ +void wtp_free_reference_last_request(void) { + capwap_fragment_free(g_wtp.requestfragmentpacket); +} + +/* */ +void wtp_free_reference_last_response(void) { + capwap_fragment_free(g_wtp.responsefragmentpacket); + memset(&g_wtp.lastrecvpackethash[0], 0, sizeof(g_wtp.lastrecvpackethash)); +} diff --git a/src/wtp/wtp_dfa.h b/src/wtp/wtp_dfa.h new file mode 100644 index 0000000..73f5d29 --- /dev/null +++ b/src/wtp/wtp_dfa.h @@ -0,0 +1,76 @@ +#ifndef __WTP_DFA_HEADER__ +#define __WTP_DFA_HEADER__ + +#include "wtp.h" +#include "capwap_network.h" +#include "capwap_protocol.h" +#include "capwap_element.h" + +/* Execute WTP DFA */ +#define WTP_DFA_NO_PACKET 1 +#define WTP_DFA_ACCEPT_PACKET 2 +#define WTP_DFA_DROP_PACKET 3 + +/* */ +struct wtp_discovery_response { + struct sockaddr_storage acaddr; + struct capwap_build_packet* packet; + struct capwap_element_discovery_response discoveryresponse; +}; + +void wtp_free_discovery_response_array(void); + +/* */ +int wtp_bio_send(struct capwap_dtls* dtls, char* buffer, int length, void* param); + +/* */ +int wtp_teardown_connection(struct timeout_control* timeout); + +/* */ +void wtp_free_reference_last_request(void); +void wtp_free_reference_last_response(void); + +/* State machine */ +int wtp_dfa_execute(void); +void wtp_dfa_change_state(int state); + +int wtp_dfa_state_idle(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_idle_to_discovery(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_idle_to_dtlssetup(struct capwap_packet* packet, struct timeout_control* timeout); + +int wtp_dfa_state_discovery(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_discovery_to_sulking(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_discovery_to_dtlssetup(struct capwap_packet* packet, struct timeout_control* timeout); + +int wtp_dfa_state_sulking(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_sulking_to_idle(struct capwap_packet* packet, struct timeout_control* timeout); + +int wtp_dfa_state_dtlssetup(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_dtlsconnect(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_dtlsconnect_to_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout); + +int wtp_dfa_state_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_dtlsteardown_to_sulking(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_dtlsteardown_to_idle(struct capwap_packet* packet, struct timeout_control* timeout); + +int wtp_dfa_state_dtlsconnect_to_join(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_join(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_join_to_configure(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_join_to_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout); + +int wtp_dfa_state_imagedata_to_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout); + +int wtp_dfa_state_configure(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_configure_to_datacheck(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_configure_to_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout); + +int wtp_dfa_state_datacheck(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_datacheck_to_run(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_datacheck_to_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout); + +int wtp_dfa_state_run(struct capwap_packet* packet, struct timeout_control* timeout); +int wtp_dfa_state_run_to_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout); + +int wtp_dfa_state_reset(struct capwap_packet* packet, struct timeout_control* timeout); + +#endif /* __WTP_DFA_HEADER__ */ diff --git a/src/wtp/wtp_dfa_configure.c b/src/wtp/wtp_dfa_configure.c new file mode 100644 index 0000000..b8535e8 --- /dev/null +++ b/src/wtp/wtp_dfa_configure.c @@ -0,0 +1,181 @@ +#include "wtp.h" +#include "capwap_dfa.h" +#include "capwap_element.h" +#include "capwap_array.h" +#include "capwap_list.h" +#include "wtp_dfa.h" + +/* */ +static unsigned long wtp_configure_ac(struct capwap_element_configurationstatus_response* configureresponse) { + /* TODO: gestione richiesta */ + + /* */ + g_wtp.dfa.rfcMaxDiscoveryInterval = configureresponse->timers->discovery; + g_wtp.dfa.rfcEchoInterval = configureresponse->timers->echorequest; + + return CAPWAP_CONFIGURE_TO_DATA_CHECK_STATE; +} + +/* */ +int wtp_dfa_state_configure(struct capwap_packet* packet, struct timeout_control* timeout) { + int status = WTP_DFA_ACCEPT_PACKET; + + ASSERT(timeout != NULL); + + if (packet) { + if (!capwap_compare_ip(&g_wtp.acctrladdress, &packet->remoteaddr)) { + struct capwap_build_packet* buildpacket; + + /* Parsing packet */ + buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket); + if (buildpacket) { + if (!capwap_build_packet_validate(buildpacket, NULL)) { + unsigned short binding; + + /* */ + binding = GET_WBID_HEADER(&buildpacket->header); + if ((binding == g_wtp.binding) && (ntohl(buildpacket->ctrlmsg.type) == CAPWAP_CONFIGURATION_STATUS_RESPONSE) && ((g_wtp.localseqnumber - 1) == buildpacket->ctrlmsg.seq)) { + struct capwap_element_configurationstatus_response configureresponse; + + /* Valid packet, free request packet */ + wtp_free_reference_last_request(); + + /* Configuration status response info */ + capwap_init_element_configurationstatus_response(&configureresponse, binding); + + /* Parsing elements list */ + if (capwap_parsing_element_configurationstatus_response(&configureresponse, buildpacket->elementslist->first)) { + wtp_dfa_change_state(wtp_configure_ac(&configureresponse)); + status = WTP_DFA_NO_PACKET; + } + + /* Free join response */ + capwap_free_element_configurationstatus_response(&configureresponse, binding); + } + } + + /* Free */ + capwap_build_packet_free(buildpacket); + } + } + } else { + int i; + + /* No Configuration status response received */ + g_wtp.dfa.rfcRetransmitCount++; + if (g_wtp.dfa.rfcRetransmitCount >= g_wtp.dfa.rfcMaxRetransmit) { + /* Timeout join state */ + wtp_free_reference_last_request(); + wtp_dfa_change_state(CAPWAP_CONFIGURE_TO_DTLS_TEARDOWN_STATE); + status = WTP_DFA_NO_PACKET; + } else { + /* Retransmit configuration request */ + for (i = 0; i < g_wtp.requestfragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(g_wtp.requestfragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&g_wtp.ctrldtls, g_wtp.acctrlsock.socket[g_wtp.acctrlsock.type], txpacket->header, txpacket->packetsize, &g_wtp.wtpctrladdress, &g_wtp.acctrladdress)) { + capwap_logging_debug("Warning: error to send configuration status request packet"); + break; + } + } + + /* Update timeout */ + capwap_set_timeout(g_wtp.dfa.rfcRetransmitInterval, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } + } + + return status; +} + +int wtp_dfa_state_configure_to_datacheck(struct capwap_packet* packet, struct timeout_control* timeout) { + unsigned long i; + int result = -1; + int status = WTP_DFA_NO_PACKET; + struct capwap_build_packet* buildpacket; + struct capwap_resultcode_element resultcode; + + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + /* Build packet */ + buildpacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, g_wtp.binding); + buildpacket->isctrlmsg = 1; + + /* Prepare change state event request */ + capwap_build_packet_set_control_message_type(buildpacket, CAPWAP_CHANGE_STATE_EVENT_REQUEST, g_wtp.localseqnumber++); + + for (i = 0; i < g_wtp.radios->count; i++) { + struct wtp_radio* radio = (struct wtp_radio*)capwap_array_get_item_pointer(g_wtp.radios, i); + struct capwap_radiooprstate_element radiooprstate; + + radiooprstate.radioid = (unsigned char)(i + 1); + radiooprstate.state = ((radio->status == WTP_RADIO_ENABLED) ? CAPWAP_RADIO_OPERATIONAL_STATE_ENABLED : CAPWAP_RADIO_OPERATIONAL_STATE_DISABLED); + + if (radiooprstate.state == WTP_RADIO_ENABLED) { + radiooprstate.cause = CAPWAP_RADIO_OPERATIONAL_CAUSE_NORMAL; + } else if (radiooprstate.state == WTP_RADIO_DISABLED) { + radiooprstate.cause = CAPWAP_RADIO_OPERATIONAL_CAUSE_ADMINSET; + } else if (radiooprstate.state == WTP_RADIO_HWFAILURE) { + radiooprstate.cause = CAPWAP_RADIO_OPERATIONAL_CAUSE_RADIOFAILURE; + } else if (radiooprstate.state == WTP_RADIO_SWFAILURE) { + radiooprstate.cause = CAPWAP_RADIO_OPERATIONAL_CAUSE_SOFTWAREFAILURE; + } + + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_RADIOOPRSTATE_ELEMENT(&radiooprstate)); + } + + resultcode.code = CAPWAP_RESULTCODE_SUCCESS; + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_RESULTCODE_ELEMENT(&resultcode)); + /* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */ + + /* Create change state event request packet */ + if (!capwap_build_packet_validate(buildpacket, NULL)) { + wtp_free_reference_last_request(); + result = capwap_fragment_build_packet(buildpacket, g_wtp.requestfragmentpacket, g_wtp.mtu, g_wtp.fragmentid); + if (result == 1) { + g_wtp.fragmentid++; + } + } else { + capwap_logging_debug("Warning: build invalid change state event request packet"); + } + + capwap_build_packet_free(buildpacket); + + /* Send change state event request to AC */ + if (result >= 0) { + for (i = 0; i < g_wtp.requestfragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(g_wtp.requestfragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&g_wtp.ctrldtls, g_wtp.acctrlsock.socket[g_wtp.acctrlsock.type], txpacket->header, txpacket->packetsize, &g_wtp.wtpctrladdress, &g_wtp.acctrladdress)) { + capwap_logging_debug("Warning: error to send change state event request packet"); + result = -1; + break; + } + } + + if (result == -1) { + /* Error to send packets */ + wtp_free_reference_last_request(); + wtp_dfa_change_state(CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE); + } else { + g_wtp.dfa.rfcRetransmitCount = 0; + capwap_set_timeout(g_wtp.dfa.rfcRetransmitInterval, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + wtp_dfa_change_state(CAPWAP_DATA_CHECK_STATE); + status = WTP_DFA_ACCEPT_PACKET; + } + } else { + wtp_dfa_change_state(CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE); + } + + return status; +} + +/* */ +int wtp_dfa_state_configure_to_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(packet == NULL); + ASSERT(timeout != NULL); + + return wtp_teardown_connection(timeout); +} diff --git a/src/wtp/wtp_dfa_datacheck.c b/src/wtp/wtp_dfa_datacheck.c new file mode 100644 index 0000000..da6a19f --- /dev/null +++ b/src/wtp/wtp_dfa_datacheck.c @@ -0,0 +1,165 @@ +#include "wtp.h" +#include "capwap_dfa.h" +#include "capwap_element.h" +#include "wtp_dfa.h" + +/* */ +static unsigned long wtp_datacheck_ac(struct capwap_element_changestateevent_response* changestateresponse) { + /* TODO: gestione richiesta */ + + return CAPWAP_DATA_CHECK_TO_RUN_STATE; +} + +/* */ +int wtp_dfa_state_datacheck(struct capwap_packet* packet, struct timeout_control* timeout) { + int status = WTP_DFA_ACCEPT_PACKET; + + ASSERT(timeout != NULL); + + if (packet) { + if (!capwap_compare_ip(&g_wtp.acctrladdress, &packet->remoteaddr)) { + struct capwap_build_packet* buildpacket; + + /* Parsing packet */ + buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket); + if (buildpacket) { + if (!capwap_build_packet_validate(buildpacket, NULL)) { + unsigned short binding; + + /* */ + binding = GET_WBID_HEADER(&buildpacket->header); + if ((binding == g_wtp.binding) && (ntohl(buildpacket->ctrlmsg.type) == CAPWAP_CHANGE_STATE_EVENT_RESPONSE) && ((g_wtp.localseqnumber - 1) == buildpacket->ctrlmsg.seq)) { + struct capwap_element_changestateevent_response changestateresponse; + + /* Valid packet, free request packet */ + wtp_free_reference_last_request(); + + /* Configuration status response info */ + capwap_init_element_changestateevent_response(&changestateresponse, binding); + + /* Parsing elements list */ + if (capwap_parsing_element_changestateevent_response(&changestateresponse, buildpacket->elementslist->first)) { + wtp_dfa_change_state(wtp_datacheck_ac(&changestateresponse)); + status = WTP_DFA_NO_PACKET; + } + + /* Free join response */ + capwap_free_element_changestateevent_response(&changestateresponse, binding); + } + } + + /* Free */ + capwap_build_packet_free(buildpacket); + } + } + } else { + int i; + + /* No change state response received */ + g_wtp.dfa.rfcRetransmitCount++; + if (g_wtp.dfa.rfcRetransmitCount >= g_wtp.dfa.rfcMaxRetransmit) { + /* Timeout join state */ + wtp_free_reference_last_request(); + wtp_dfa_change_state(CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE); + status = WTP_DFA_NO_PACKET; + } else { + /* Retransmit change state request */ + for (i = 0; i < g_wtp.requestfragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(g_wtp.requestfragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&g_wtp.ctrldtls, g_wtp.acctrlsock.socket[g_wtp.acctrlsock.type], txpacket->header, txpacket->packetsize, &g_wtp.wtpctrladdress, &g_wtp.acctrladdress)) { + capwap_logging_debug("Warning: error to send change state request packet"); + break; + } + } + + /* Update timeout */ + capwap_set_timeout(g_wtp.dfa.rfcRetransmitInterval, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } + } + + return status; +} + +/* */ +int wtp_dfa_state_datacheck_to_run(struct capwap_packet* packet, struct timeout_control* timeout) { + int result; + int status = WTP_DFA_ACCEPT_PACKET; + struct capwap_build_packet* buildpacket; + capwap_fragment_packet_array* txfragpacket; + + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + /* If need, create DTLS Data channel crypted */ + if (g_wtp.dtlsdatapolicy & CAPWAP_ACDESC_DTLS_DATA_CHANNEL_ENABLED) { + if (!g_wtp.datadtls.enable) { + /* Create DTLS data session before send data keepalive */ + if (capwap_crypt_createsession(&g_wtp.datadtls, CAPWAP_DTLS_DATA_SESSION, &g_wtp.dtlscontext, wtp_bio_send, NULL)) { + if (capwap_crypt_open(&g_wtp.datadtls, &g_wtp.acdataaddress) == CAPWAP_HANDSHAKE_CONTINUE) { + /* Wait complete dtls handshake */ + capwap_set_timeout(g_wtp.dfa.rfcWaitDTLS, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + return WTP_DFA_ACCEPT_PACKET; + } else { + /* TODO error */ + } + } else { + /* TODO error */ + } + } else if (g_wtp.datadtls.action != CAPWAP_DTLS_ACTION_DATA) { + wtp_dfa_change_state(CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE); + return WTP_DFA_NO_PACKET; + } + } + + /* Build packet */ + buildpacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, CAPWAP_WIRELESS_BINDING_NONE); + buildpacket->isctrlmsg = 0; + + /* */ + SET_FLAG_K_HEADER(&buildpacket->header, 1); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_SESSIONID_ELEMENT(&g_wtp.sessionid)); + + txfragpacket = capwap_array_create(sizeof(struct capwap_packet), 0); + result = capwap_fragment_build_packet(buildpacket, txfragpacket, CAPWAP_DONT_FRAGMENT, 0); + if (!result) { + struct capwap_packet* txpacket; + + ASSERT(txfragpacket->count == 1); + + txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(txfragpacket, 0); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&g_wtp.datadtls, g_wtp.acdatasock.socket[g_wtp.acdatasock.type], txpacket->header, txpacket->packetsize, &g_wtp.wtpdataaddress, &g_wtp.acdataaddress)) { + capwap_logging_debug("Warning: error to send data channel keepalive packet"); + result = -1; + } + } + + capwap_fragment_free(txfragpacket); + capwap_array_free(txfragpacket); + capwap_build_packet_free(buildpacket); + + /* Send Configuration Status request to AC */ + if (!result) { + capwap_kill_timeout(timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + capwap_set_timeout(g_wtp.dfa.rfcEchoInterval, timeout, CAPWAP_TIMER_CONTROL_ECHO); + capwap_set_timeout(g_wtp.dfa.rfcDataChannelDeadInterval, timeout, CAPWAP_TIMER_DATA_KEEPALIVEDEAD); + wtp_dfa_change_state(CAPWAP_RUN_STATE); + } else { + wtp_dfa_change_state(CAPWAP_DATA_CHECK_TO_DTLS_TEARDOWN_STATE); + status = WTP_DFA_NO_PACKET; + } + + /* */ + return status; +} + +/* */ +int wtp_dfa_state_datacheck_to_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(packet == NULL); + ASSERT(timeout != NULL); + + return wtp_teardown_connection(timeout); +} diff --git a/src/wtp/wtp_dfa_discovery.c b/src/wtp/wtp_dfa_discovery.c new file mode 100644 index 0000000..2c9625d --- /dev/null +++ b/src/wtp/wtp_dfa_discovery.c @@ -0,0 +1,298 @@ +#include "wtp.h" +#include "capwap_dfa.h" +#include "capwap_element.h" +#include "capwap_array.h" +#include "capwap_list.h" +#include "wtp_dfa.h" + +/* */ +void wtp_free_discovery_response_array(void) { + int i; + + /* Free items */ + for (i = 0; i < g_wtp.acdiscoveryresponse->count; i++) { + struct wtp_discovery_response* response = (struct wtp_discovery_response*)capwap_array_get_item_pointer(g_wtp.acdiscoveryresponse, i); + + capwap_free_element_discovery_response(&response->discoveryresponse, GET_WBID_HEADER(&response->packet->header)); + capwap_build_packet_free(response->packet); + } + + /* Remove all items */ + capwap_array_resize(g_wtp.acdiscoveryresponse, 0); +} + +/* */ +int wtp_dfa_state_discovery(struct capwap_packet* packet, struct timeout_control* timeout) { + int status = WTP_DFA_ACCEPT_PACKET; + + ASSERT(timeout != NULL); + + if (packet) { + struct capwap_build_packet* buildpacket; + + buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket); + if (buildpacket) { + if (capwap_build_packet_validate(buildpacket, NULL)) { + capwap_build_packet_free(buildpacket); /* Invalid packet */ + } else { + unsigned short binding; + + /* */ + binding = GET_WBID_HEADER(&buildpacket->header); + if ((binding != g_wtp.binding) || (ntohl(buildpacket->ctrlmsg.type) != CAPWAP_DISCOVERY_RESPONSE) || ((g_wtp.localseqnumber - 1) != buildpacket->ctrlmsg.seq)) { + capwap_build_packet_free(buildpacket); /* Invalid packet */ + } else { + struct wtp_discovery_response* response = (struct wtp_discovery_response*)capwap_array_get_item_pointer(g_wtp.acdiscoveryresponse, g_wtp.acdiscoveryresponse->count); + + /* Discovery response info */ + memcpy(&response->acaddr, &packet->remoteaddr, sizeof(struct sockaddr_storage)); + response->packet = buildpacket; + capwap_init_element_discovery_response(&response->discoveryresponse, binding); + + /* Parsing elements list */ + capwap_parsing_element_discovery_response(&response->discoveryresponse, buildpacket->elementslist->first); + } + } + } + } else if (g_wtp.acdiscoveryresponse->count > 0) { + int i, j, w; + int countwtp = -1; + int indexpreferred = -1; + + struct sockaddr_storage checkaddr; + struct sockaddr_in* checkaddripv4; + struct sockaddr_in6* checkaddripv6; + + /* */ + g_wtp.acctrladdress.ss_family = AF_UNSPEC; + + /* Selected by preferred or less WTP by AC */ + for (i = 0; i < g_wtp.acdiscoveryresponse->count; i++) { + struct wtp_discovery_response* response = (struct wtp_discovery_response*)capwap_array_get_item_pointer(g_wtp.acdiscoveryresponse, i); + + /* AC with IPv4 */ + if ((g_wtp.net.sock_family == AF_UNSPEC) || (g_wtp.net.sock_family == AF_INET)) { + for (w = 0; w < response->discoveryresponse.controlipv4->count; w++) { + struct capwap_controlipv4_element* controlipv4 = *(struct capwap_controlipv4_element**)capwap_array_get_item_pointer(response->discoveryresponse.controlipv4, w); + + /* Create IPv4 address */ + checkaddripv4 = (struct sockaddr_in*)&checkaddr; + checkaddripv4->sin_family = AF_INET; + checkaddripv4->sin_port = htons(CAPWAP_CONTROL_PORT); + memcpy(&checkaddripv4->sin_addr, &controlipv4->address, sizeof(struct in_addr)); + + /* Check for preferred AC */ + for (j = 0; j < ((indexpreferred != -1) ? indexpreferred : g_wtp.acpreferedarray->count); j++) { + struct sockaddr_storage* acpreferredaddr = (struct sockaddr_storage*)capwap_array_get_item_pointer(g_wtp.acpreferedarray, j); + + if (!capwap_compare_ip(acpreferredaddr, &checkaddr)) { + indexpreferred = j; + memcpy(&g_wtp.acctrladdress, &checkaddr, sizeof(struct sockaddr_storage)); + capwap_get_network_socket(&g_wtp.net, &g_wtp.acctrlsock, capwap_get_socket(&g_wtp.net, g_wtp.acctrladdress.ss_family, IPPROTO_UDP, 1)); + capwap_get_network_socket(&g_wtp.net, &g_wtp.acdatasock, capwap_get_socket(&g_wtp.net, g_wtp.acctrladdress.ss_family, (g_wtp.transport.type == CAPWAP_UDP_TRANSPORT ? IPPROTO_UDP : IPPROTO_UDPLITE), CAPWAP_DATA_SOCKET)); + break; + } + } + + /* Check by number of WTP */ + if (indexpreferred == -1) { + if ((countwtp == -1) || (countwtp > controlipv4->wtpcount)) { + countwtp = controlipv4->wtpcount; + memcpy(&g_wtp.acctrladdress, &checkaddr, sizeof(struct sockaddr_storage)); + capwap_get_network_socket(&g_wtp.net, &g_wtp.acctrlsock, capwap_get_socket(&g_wtp.net, g_wtp.acctrladdress.ss_family, IPPROTO_UDP, 1)); + capwap_get_network_socket(&g_wtp.net, &g_wtp.acdatasock, capwap_get_socket(&g_wtp.net, g_wtp.acctrladdress.ss_family, (g_wtp.transport.type == CAPWAP_UDP_TRANSPORT ? IPPROTO_UDP : IPPROTO_UDPLITE), CAPWAP_DATA_SOCKET)); + } + } + } + } + + /* AC with IPv6 */ + if ((g_wtp.net.sock_family == AF_UNSPEC) || (g_wtp.net.sock_family == AF_INET6)) { + for (w = 0; w < response->discoveryresponse.controlipv6->count; w++) { + struct capwap_controlipv6_element* controlipv6 = *(struct capwap_controlipv6_element**)capwap_array_get_item_pointer(response->discoveryresponse.controlipv6, w); + + /* Create IPv6 address */ + checkaddripv6 = (struct sockaddr_in6*)&checkaddr; + checkaddripv6->sin6_family = AF_INET6; + checkaddripv6->sin6_port = htons(CAPWAP_CONTROL_PORT); + memcpy(&checkaddripv6->sin6_addr, &controlipv6->address, sizeof(struct in6_addr)); + + /* Check for preferred AC */ + for (j = 0; j < ((indexpreferred != -1) ? indexpreferred : g_wtp.acpreferedarray->count); j++) { + struct sockaddr_storage* acpreferredaddr = (struct sockaddr_storage*)capwap_array_get_item_pointer(g_wtp.acpreferedarray, j); + + if (!capwap_compare_ip(acpreferredaddr, &checkaddr)) { + indexpreferred = j; + memcpy(&g_wtp.acctrladdress, &checkaddr, sizeof(struct sockaddr_storage)); + capwap_get_network_socket(&g_wtp.net, &g_wtp.acctrlsock, capwap_get_socket(&g_wtp.net, g_wtp.acctrladdress.ss_family, IPPROTO_UDP, 1)); + capwap_get_network_socket(&g_wtp.net, &g_wtp.acdatasock, capwap_get_socket(&g_wtp.net, g_wtp.acctrladdress.ss_family, (g_wtp.transport.type == CAPWAP_UDP_TRANSPORT ? IPPROTO_UDP : IPPROTO_UDPLITE), CAPWAP_DATA_SOCKET)); + break; + } + } + + /* Check by number of WTP */ + if (indexpreferred == -1) { + if ((countwtp == -1) || (countwtp > controlipv6->wtpcount)) { + countwtp = controlipv6->wtpcount; + memcpy(&g_wtp.acctrladdress, &checkaddr, sizeof(struct sockaddr_storage)); + capwap_get_network_socket(&g_wtp.net, &g_wtp.acctrlsock, capwap_get_socket(&g_wtp.net, g_wtp.acctrladdress.ss_family, IPPROTO_UDP, 1)); + capwap_get_network_socket(&g_wtp.net, &g_wtp.acdatasock, capwap_get_socket(&g_wtp.net, g_wtp.acctrladdress.ss_family, (g_wtp.transport.type == CAPWAP_UDP_TRANSPORT ? IPPROTO_UDP : IPPROTO_UDPLITE), CAPWAP_DATA_SOCKET)); + } + } + } + } + } + + /* Free memory */ + wtp_free_discovery_response_array(); + + /* Change state if found AC */ + if (g_wtp.acctrladdress.ss_family != AF_UNSPEC) { + memcpy(&g_wtp.acdataaddress, &g_wtp.acctrladdress, sizeof(struct sockaddr_storage)); + CAPWAP_SET_NETWORK_PORT(&g_wtp.acdataaddress, CAPWAP_GET_NETWORK_PORT(&g_wtp.acdataaddress) + 1); + wtp_dfa_change_state(CAPWAP_DISCOVERY_TO_DTLS_SETUP_STATE); + } + + status = WTP_DFA_NO_PACKET; + } else { + int i; + int result; + struct capwap_build_packet* buildpacket; + + /* No Discovery response received */ + g_wtp.dfa.rfcDiscoveryCount++; + if (g_wtp.dfa.rfcDiscoveryCount >= g_wtp.dfa.rfcMaxDiscoveries) { + /* Timeout discovery state */ + wtp_dfa_change_state(CAPWAP_DISCOVERY_TO_SULKING_STATE); + status = WTP_DFA_NO_PACKET; + } else { + /* Update status radio */ + g_wtp.descriptor.radiosinuse = wtp_update_radio_in_use(); + + /* Build packet */ + buildpacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, g_wtp.binding); + buildpacket->isctrlmsg = 1; + + /* Prepare discovery request */ + capwap_build_packet_set_control_message_type(buildpacket, CAPWAP_DISCOVERY_REQUEST, g_wtp.localseqnumber++); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_DISCOVERYTYPE_ELEMENT(&g_wtp.discoverytype)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_WTPBOARDDATA_ELEMENT(&g_wtp.boarddata)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_WTPDESCRIPTOR_ELEMENT(&g_wtp.descriptor)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_WTPFRAMETUNNELMODE_ELEMENT(&g_wtp.mactunnel)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_WTPMACTYPE_ELEMENT(&g_wtp.mactype)); + + if (g_wtp.binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + for (i = 0; i < g_wtp.radios->count; i++) { + struct wtp_radio* radio = (struct wtp_radio*)capwap_array_get_item_pointer(g_wtp.radios, i); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_80211_WTPRADIOINFORMATION_ELEMENT(&radio->radioinformation)); + } + } else { + capwap_logging_debug("Unknown capwap binding"); + } + + /* CAPWAP_CREATE_MTUDISCOVERYPADDING_ELEMENT */ /* TODO */ + /* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */ + + /* Create discovery request packet */ + if (!capwap_build_packet_validate(buildpacket, NULL)) { + wtp_free_reference_last_request(); + result = capwap_fragment_build_packet(buildpacket, g_wtp.requestfragmentpacket, g_wtp.mtu, g_wtp.fragmentid); + if (result == 1) { + g_wtp.fragmentid++; + } + } else { + result = -1; + capwap_logging_debug("Warning: build invalid discovery request packet"); + } + + capwap_build_packet_free(buildpacket); + + /* Send discovery request to AC */ + if (result >= 0) { + int i; + + /* Send broadcast packet to all socket */ + for (i = 0; i < g_wtp.requestfragmentpacket->count; i++) { + int j; + struct capwap_packet* packet = (struct capwap_packet*)capwap_array_get_item_pointer(g_wtp.requestfragmentpacket, i); + + ASSERT(packet != NULL); + + for (j = 0; j < g_wtp.acdiscoveryarray->count; j++) { + int sock; + struct sockaddr_storage* sendtoaddr = (struct sockaddr_storage*)capwap_array_get_item_pointer(g_wtp.acdiscoveryarray, j); + + sock = capwap_get_socket(&g_wtp.net, sendtoaddr->ss_family, IPPROTO_UDP, 1); + if (sock >= 0) { + if (!capwap_sendto(sock, packet->header, packet->packetsize, NULL, sendtoaddr)) { + capwap_logging_debug("Warning: error to send discovery request packet"); + break; + } + } + } + } + + /* Don't buffering a packets sent */ + wtp_free_reference_last_request(); + } + + /* Wait before send another Discovery Request */ + capwap_set_timeout(g_wtp.dfa.rfcDiscoveryInterval, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } + } + + return status; +} + +/* */ +int wtp_dfa_state_discovery_to_sulking(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + capwap_set_timeout(g_wtp.dfa.rfcSilentInterval, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + wtp_dfa_change_state(CAPWAP_SULKING_STATE); + + return WTP_DFA_DROP_PACKET; +} + +/* */ +int wtp_dfa_state_discovery_to_dtlssetup(struct capwap_packet* packet, struct timeout_control* timeout) { + int status = WTP_DFA_ACCEPT_PACKET; + + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + /* Retrieve local address */ + if (!capwap_get_localaddress_by_remoteaddress(&g_wtp.wtpctrladdress, &g_wtp.acctrladdress, g_wtp.net.bind_interface, (!(g_wtp.net.bind_ctrl_flags & CAPWAP_IPV6ONLY_FLAG) ? 1 : 0))) { + wtp_dfa_change_state(CAPWAP_DISCOVERY_TO_SULKING_STATE); + status = WTP_DFA_NO_PACKET; + } else { + struct sockaddr_storage sockinfo; + socklen_t sockinfolen = sizeof(struct sockaddr_storage); + + memset(&sockinfo, 0, sizeof(struct sockaddr_storage)); + if (getsockname(g_wtp.acctrlsock.socket[g_wtp.acctrlsock.type], (struct sockaddr*)&sockinfo, &sockinfolen) < 0) { + wtp_dfa_change_state(CAPWAP_DTLS_SETUP_TO_SULKING_STATE); + status = WTP_DFA_NO_PACKET; + } else { + CAPWAP_SET_NETWORK_PORT(&g_wtp.wtpctrladdress, CAPWAP_GET_NETWORK_PORT(&sockinfo)); + + /* */ + memcpy(&g_wtp.wtpdataaddress, &g_wtp.wtpctrladdress, sizeof(struct sockaddr_storage)); + CAPWAP_SET_NETWORK_PORT(&g_wtp.wtpdataaddress, CAPWAP_GET_NETWORK_PORT(&g_wtp.wtpdataaddress) + 1); + + /* */ + if (!g_wtp.enabledtls) { + /* Bypass DTLS connection */ + wtp_dfa_change_state(CAPWAP_DTLS_CONNECT_TO_JOIN_STATE); + status = WTP_DFA_NO_PACKET; + } else { + /* Create DTLS connection */ + wtp_dfa_change_state(CAPWAP_DTLS_SETUP_STATE); + status = WTP_DFA_NO_PACKET; + } + } + } + + return status; +} diff --git a/src/wtp/wtp_dfa_dtls.c b/src/wtp/wtp_dfa_dtls.c new file mode 100644 index 0000000..ebc1545 --- /dev/null +++ b/src/wtp/wtp_dfa_dtls.c @@ -0,0 +1,123 @@ +#include "wtp.h" +#include "capwap_dfa.h" +#include "wtp_dfa.h" + +/* DTLS BIO send */ +int wtp_bio_send(struct capwap_dtls* dtls, char* buffer, int length, void* param) { + struct capwap_socket* socket = ((dtls->session == CAPWAP_DTLS_CONTROL_SESSION) ? &g_wtp.acctrlsock : &g_wtp.acdatasock); + struct sockaddr_storage* wtpaddress = ((dtls->session == CAPWAP_DTLS_CONTROL_SESSION) ? &g_wtp.wtpctrladdress : &g_wtp.wtpdataaddress); + struct sockaddr_storage* acaddress = ((dtls->session == CAPWAP_DTLS_CONTROL_SESSION) ? &g_wtp.acctrladdress : &g_wtp.acdataaddress); + + return capwap_sendto(socket->socket[socket->type], buffer, length, wtpaddress, acaddress); +} + +/* */ +int wtp_dfa_state_dtlssetup(struct capwap_packet* packet, struct timeout_control* timeout) { + int status = WTP_DFA_ACCEPT_PACKET; + + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + /* Create DTLS session */ + if (!capwap_crypt_createsession(&g_wtp.ctrldtls, CAPWAP_DTLS_CONTROL_SESSION, &g_wtp.dtlscontext, wtp_bio_send, NULL)) { + wtp_dfa_change_state(CAPWAP_DTLS_SETUP_TO_IDLE_STATE); + status = WTP_DFA_NO_PACKET; + } else { + if (capwap_crypt_open(&g_wtp.ctrldtls, &g_wtp.acctrladdress) == CAPWAP_HANDSHAKE_ERROR) { + wtp_dfa_change_state(CAPWAP_DTLS_SETUP_TO_IDLE_STATE); + status = WTP_DFA_NO_PACKET; + } else { + wtp_dfa_change_state(CAPWAP_DTLS_CONNECT_STATE); + capwap_set_timeout(g_wtp.dfa.rfcWaitDTLS, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } + } + + return status; +} + +/* */ +int wtp_dfa_state_dtlsconnect(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + wtp_dfa_change_state(CAPWAP_DTLS_CONNECT_TO_DTLS_TEARDOWN_STATE); + return WTP_DFA_NO_PACKET; +} + +/* */ +int wtp_dfa_state_dtlsconnect_to_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(packet == NULL); + ASSERT(timeout != NULL); + + return wtp_teardown_connection(timeout); +} + +/* Teardown connection */ +int wtp_teardown_connection(struct timeout_control* timeout) { + ASSERT(timeout != NULL); + + /* DTSL Control */ + if (g_wtp.ctrldtls.enable) { + capwap_crypt_close(&g_wtp.ctrldtls); + } + + /* DTLS Data */ + if (g_wtp.datadtls.enable) { + capwap_crypt_close(&g_wtp.datadtls); + } + + /* */ + capwap_killall_timeout(timeout); + capwap_set_timeout(g_wtp.dfa.rfcDTLSSessionDelete, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + wtp_dfa_change_state(CAPWAP_DTLS_TEARDOWN_STATE); + return WTP_DFA_DROP_PACKET; +} + +/* */ +int wtp_dfa_state_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + /* Free and reset resource */ + if (g_wtp.ctrldtls.enable) { + capwap_crypt_freesession(&g_wtp.ctrldtls); + } + + if (g_wtp.datadtls.enable) { + capwap_crypt_freesession(&g_wtp.datadtls); + } + + /* */ + wtp_free_reference_last_request(); + wtp_free_reference_last_response(); + + /* */ + if ((g_wtp.dfa.rfcFailedDTLSSessionCount >= g_wtp.dfa.rfcMaxFailedDTLSSessionRetry) || (g_wtp.dfa.rfcFailedDTLSAuthFailCount >= g_wtp.dfa.rfcMaxFailedDTLSSessionRetry)) { + wtp_dfa_change_state(CAPWAP_DTLS_TEARDOWN_TO_SULKING_STATE); + } else { + wtp_dfa_change_state(CAPWAP_DTLS_TEARDOWN_TO_IDLE_STATE); + } + + /* TODO controllare se è richiesto il ravvio del sistema */ + return WTP_DFA_NO_PACKET; +} + +/* */ +int wtp_dfa_state_dtlsteardown_to_sulking(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + capwap_set_timeout(g_wtp.dfa.rfcSilentInterval, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + wtp_dfa_change_state(CAPWAP_SULKING_STATE); + + return WTP_DFA_DROP_PACKET; +} + +/* */ +int wtp_dfa_state_dtlsteardown_to_idle(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + wtp_dfa_change_state(CAPWAP_IDLE_STATE); + return WTP_DFA_NO_PACKET; +} diff --git a/src/wtp/wtp_dfa_idle.c b/src/wtp/wtp_dfa_idle.c new file mode 100644 index 0000000..cc35a4e --- /dev/null +++ b/src/wtp/wtp_dfa_idle.c @@ -0,0 +1,92 @@ +#include "wtp.h" +#include "capwap_dfa.h" +#include "wtp_dfa.h" + +/* */ +int wtp_dfa_state_idle(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + if (!g_wtp.acdiscoveryrequest && (g_wtp.acpreferedarray->count > 0)) { + /* Found in configuration file the AC address */ + memcpy(&g_wtp.acctrladdress, capwap_array_get_item_pointer(g_wtp.acpreferedarray, g_wtp.acpreferedselected), sizeof(struct sockaddr_storage)); + memcpy(&g_wtp.acdataaddress, &g_wtp.acctrladdress, sizeof(struct sockaddr_storage)); + CAPWAP_SET_NETWORK_PORT(&g_wtp.acdataaddress, CAPWAP_GET_NETWORK_PORT(&g_wtp.acdataaddress) + 1); + + /* Configure socket */ + capwap_get_network_socket(&g_wtp.net, &g_wtp.acctrlsock, capwap_get_socket(&g_wtp.net, g_wtp.acctrladdress.ss_family, IPPROTO_UDP, CAPWAP_CTRL_SOCKET)); + capwap_get_network_socket(&g_wtp.net, &g_wtp.acdatasock, capwap_get_socket(&g_wtp.net, g_wtp.acdataaddress.ss_family, (g_wtp.transport.type == CAPWAP_UDP_TRANSPORT ? IPPROTO_UDP : IPPROTO_UDPLITE), CAPWAP_DATA_SOCKET)); + + /* */ + g_wtp.acpreferedselected = (g_wtp.acpreferedselected + 1) % g_wtp.acpreferedarray->count; + + /* Connect */ + wtp_dfa_change_state(CAPWAP_IDLE_TO_DTLS_SETUP_STATE); + } else { + /* Search AC */ + wtp_dfa_change_state(CAPWAP_IDLE_TO_DISCOVERY_STATE); + } + + capwap_kill_timeout(timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + return WTP_DFA_NO_PACKET; +} + +/* Prepare to discovery AC */ +int wtp_dfa_state_idle_to_discovery(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + /* Set discovery interval */ + g_wtp.dfa.rfcDiscoveryInterval = capwap_get_rand(g_wtp.dfa.rfcMaxDiscoveryInterval - WTP_MIN_DISCOVERY_INTERVAL) + WTP_MIN_DISCOVERY_INTERVAL; + g_wtp.dfa.rfcDiscoveryCount = 0; + + /* Change state */ + wtp_dfa_change_state(CAPWAP_DISCOVERY_STATE); + + /* Wait before send Discovery Request */ + capwap_set_timeout(g_wtp.dfa.rfcDiscoveryInterval, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + + return WTP_DFA_DROP_PACKET; +} + +/* Prepare to connect with AC */ +int wtp_dfa_state_idle_to_dtlssetup(struct capwap_packet* packet, struct timeout_control* timeout) { + int status = WTP_DFA_ACCEPT_PACKET; + + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + /* Retrieve local address */ + if (!capwap_get_localaddress_by_remoteaddress(&g_wtp.wtpctrladdress, &g_wtp.acctrladdress, g_wtp.net.bind_interface, (!(g_wtp.net.bind_ctrl_flags & CAPWAP_IPV6ONLY_FLAG) ? 1 : 0))) { + wtp_dfa_change_state(CAPWAP_IDLE_STATE); + status = WTP_DFA_NO_PACKET; + } else { + struct sockaddr_storage sockinfo; + socklen_t sockinfolen = sizeof(struct sockaddr_storage); + + memset(&sockinfo, 0, sizeof(struct sockaddr_storage)); + if (getsockname(g_wtp.acctrlsock.socket[g_wtp.acctrlsock.type], (struct sockaddr*)&sockinfo, &sockinfolen) < 0) { + wtp_dfa_change_state(CAPWAP_DTLS_SETUP_TO_SULKING_STATE); + status = WTP_DFA_NO_PACKET; + } else { + CAPWAP_SET_NETWORK_PORT(&g_wtp.wtpctrladdress, CAPWAP_GET_NETWORK_PORT(&sockinfo)); + + /* */ + memcpy(&g_wtp.wtpdataaddress, &g_wtp.wtpctrladdress, sizeof(struct sockaddr_storage)); + CAPWAP_SET_NETWORK_PORT(&g_wtp.wtpdataaddress, CAPWAP_GET_NETWORK_PORT(&g_wtp.wtpdataaddress) + 1); + + /* */ + if (!g_wtp.enabledtls) { + /* Bypass DTLS connection */ + wtp_dfa_change_state(CAPWAP_DTLS_CONNECT_TO_JOIN_STATE); + status = WTP_DFA_NO_PACKET; + } else { + /* Create DTLS connection */ + wtp_dfa_change_state(CAPWAP_DTLS_SETUP_STATE); + status = WTP_DFA_NO_PACKET; + } + } + } + + return status; +} diff --git a/src/wtp/wtp_dfa_imagedata.c b/src/wtp/wtp_dfa_imagedata.c new file mode 100644 index 0000000..ccc7237 --- /dev/null +++ b/src/wtp/wtp_dfa_imagedata.c @@ -0,0 +1,11 @@ +#include "wtp.h" +#include "capwap_dfa.h" +#include "wtp_dfa.h" + +/* */ +int wtp_dfa_state_imagedata_to_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(packet == NULL); + ASSERT(timeout != NULL); + + return wtp_teardown_connection(timeout); +} diff --git a/src/wtp/wtp_dfa_join.c b/src/wtp/wtp_dfa_join.c new file mode 100644 index 0000000..e25aae1 --- /dev/null +++ b/src/wtp/wtp_dfa_join.c @@ -0,0 +1,297 @@ +#include "wtp.h" +#include "capwap_dfa.h" +#include "capwap_element.h" +#include "capwap_array.h" +#include "capwap_list.h" +#include "wtp_dfa.h" + +/* */ +static unsigned long wtp_join_ac(struct capwap_element_join_response* joinresponse) { + /* TODO: gestione richiesta + CAPWAP_JOIN_TO_IMAGE_DATA_STATE <-> CAPWAP_JOIN_TO_CONFIGURE_STATE + */ + + /* Check DTLS data policy */ + if (!(g_wtp.validdtlsdatapolicy & joinresponse->acdescriptor->dtlspolicy)) { + return CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE; + } + + /* AC name associated */ + strcpy(g_wtp.acname.name, joinresponse->acname->name); + + /* DTLS data policy */ + g_wtp.dtlsdatapolicy = joinresponse->acdescriptor->dtlspolicy & g_wtp.validdtlsdatapolicy; + + return CAPWAP_JOIN_TO_CONFIGURE_STATE; +} + +/* */ +int wtp_dfa_state_dtlsconnect_to_join(struct capwap_packet* packet, struct timeout_control* timeout) { + int i; + int result = -1; + int status = WTP_DFA_NO_PACKET; + struct capwap_build_packet* buildpacket; + +#ifdef DEBUG + char sessionname[33]; +#endif + + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + /* Reset DTLS counter */ + g_wtp.dfa.rfcFailedDTLSSessionCount = 0; + + /* Update status radio */ + g_wtp.descriptor.radiosinuse = wtp_update_radio_in_use(); + + /* Generate session id */ + capwap_sessionid_generate(&g_wtp.sessionid); + +#ifdef DEBUG + capwap_sessionid_printf(&g_wtp.sessionid, sessionname); + capwap_logging_debug("Create WTP sessionid: %s", sessionname); +#endif + + /* Build packet */ + buildpacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, g_wtp.binding); + buildpacket->isctrlmsg = 1; + + /* Prepare join request */ + capwap_build_packet_set_control_message_type(buildpacket, CAPWAP_JOIN_REQUEST, g_wtp.localseqnumber++); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_LOCATION_ELEMENT(&g_wtp.location)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_WTPBOARDDATA_ELEMENT(&g_wtp.boarddata)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_WTPDESCRIPTOR_ELEMENT(&g_wtp.descriptor)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_WTPNAME_ELEMENT(&g_wtp.name)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_SESSIONID_ELEMENT(&g_wtp.sessionid)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_WTPFRAMETUNNELMODE_ELEMENT(&g_wtp.mactunnel)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_WTPMACTYPE_ELEMENT(&g_wtp.mactype)); + + if (g_wtp.binding == CAPWAP_WIRELESS_BINDING_IEEE80211) { + for (i = 0; i < g_wtp.radios->count; i++) { + struct wtp_radio* radio = (struct wtp_radio*)capwap_array_get_item_pointer(g_wtp.radios, i); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_80211_WTPRADIOINFORMATION_ELEMENT(&radio->radioinformation)); + } + } else { + capwap_logging_debug("Unknown capwap binding"); + } + + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_ECNSUPPORT_ELEMENT(&g_wtp.ecn)); + + if (g_wtp.wtpctrladdress.ss_family == AF_INET) { + struct capwap_localipv4_element addr; + + memcpy(&addr.address, &((struct sockaddr_in*)&g_wtp.wtpctrladdress)->sin_addr, sizeof(struct in_addr)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_LOCALIPV4_ELEMENT(&addr)); + } else if (g_wtp.wtpctrladdress.ss_family == AF_INET6) { + struct capwap_localipv6_element addr; + + memcpy(&addr.address, &((struct sockaddr_in6*)&g_wtp.wtpctrladdress)->sin6_addr, sizeof(struct in6_addr)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_LOCALIPV6_ELEMENT(&addr)); + } + + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_TRANSPORT_ELEMENT(&g_wtp.transport)); + /* CAPWAP_CREATE_MAXIMUMMESSAGELENGTH_ELEMENT */ /* TODO */ + /* CAPWAP_CREATE_WTPREBOOTSTATISTICS_ELEMENT */ /* TODO */ + /* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */ + + /* Create join request packet */ + if (!capwap_build_packet_validate(buildpacket, NULL)) { + wtp_free_reference_last_request(); + result = capwap_fragment_build_packet(buildpacket, g_wtp.requestfragmentpacket, g_wtp.mtu, g_wtp.fragmentid); + if (result == 1) { + g_wtp.fragmentid++; + } + } else { + capwap_logging_debug("Warning: build invalid join request packet"); + } + + capwap_build_packet_free(buildpacket); + + /* Send join request to AC */ + if (result >= 0) { + for (i = 0; i < g_wtp.requestfragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(g_wtp.requestfragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&g_wtp.ctrldtls, g_wtp.acctrlsock.socket[g_wtp.acctrlsock.type], txpacket->header, txpacket->packetsize, &g_wtp.wtpctrladdress, &g_wtp.acctrladdress)) { + capwap_logging_debug("Warning: error to send join request packet"); + result = -1; + break; + } + } + + if (result == -1) { + /* Error to send packets */ + wtp_free_reference_last_request(); + wtp_dfa_change_state(CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE); + } else { + g_wtp.dfa.rfcRetransmitCount = 0; + capwap_set_timeout(g_wtp.dfa.rfcRetransmitInterval, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + wtp_dfa_change_state(CAPWAP_JOIN_STATE); + status = WTP_DFA_ACCEPT_PACKET; + } + } else { + wtp_dfa_change_state(CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE); + } + + return status; +} + +/* */ +int wtp_dfa_state_join(struct capwap_packet* packet, struct timeout_control* timeout) { + int status = WTP_DFA_ACCEPT_PACKET; + + ASSERT(timeout != NULL); + + if (packet) { + if (!capwap_compare_ip(&g_wtp.acctrladdress, &packet->remoteaddr)) { + struct capwap_build_packet* buildpacket; + + /* Parsing packet */ + buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket); + if (buildpacket) { + if (!capwap_build_packet_validate(buildpacket, NULL)) { + unsigned short binding; + + /* */ + binding = GET_WBID_HEADER(&buildpacket->header); + if ((binding == g_wtp.binding) && (ntohl(buildpacket->ctrlmsg.type) == CAPWAP_JOIN_RESPONSE) && ((g_wtp.localseqnumber - 1) == buildpacket->ctrlmsg.seq)) { + struct capwap_element_join_response joinresponse; + + /* Valid packet, free request packet */ + wtp_free_reference_last_request(); + + /* Join response info */ + capwap_init_element_join_response(&joinresponse, binding); + + /* Parsing elements list */ + if (capwap_parsing_element_join_response(&joinresponse, buildpacket->elementslist->first)) { + wtp_dfa_change_state(wtp_join_ac(&joinresponse)); + status = WTP_DFA_NO_PACKET; + } + + /* Free join response */ + capwap_free_element_join_response(&joinresponse, binding); + } + } + + /* Free */ + capwap_build_packet_free(buildpacket); + } + } + } else { + int i; + + /* No Join response received */ + g_wtp.dfa.rfcRetransmitCount++; + if (g_wtp.dfa.rfcRetransmitCount >= g_wtp.dfa.rfcMaxRetransmit) { + /* Timeout join state */ + wtp_free_reference_last_request(); + wtp_dfa_change_state(CAPWAP_JOIN_TO_DTLS_TEARDOWN_STATE); + status = WTP_DFA_NO_PACKET; + } else { + /* Retransmit join request */ + for (i = 0; i < g_wtp.requestfragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(g_wtp.requestfragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&g_wtp.ctrldtls, g_wtp.acctrlsock.socket[g_wtp.acctrlsock.type], txpacket->header, txpacket->packetsize, &g_wtp.wtpctrladdress, &g_wtp.acctrladdress)) { + capwap_logging_debug("Warning: error to send join request packet"); + break; + } + } + + /* Update timeout */ + capwap_set_timeout(g_wtp.dfa.rfcRetransmitInterval, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } + } + + return status; +} + +/* */ +int wtp_dfa_state_join_to_configure(struct capwap_packet* packet, struct timeout_control* timeout) { + unsigned long i; + int result = -1; + int status = WTP_DFA_NO_PACKET; + struct capwap_build_packet* buildpacket; + + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + /* Build packet */ + buildpacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, g_wtp.binding); + buildpacket->isctrlmsg = 1; + + /* Prepare Configuration Status request */ + capwap_build_packet_set_control_message_type(buildpacket, CAPWAP_CONFIGURATION_STATUS_REQUEST, g_wtp.localseqnumber++); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_ACNAME_ELEMENT(&g_wtp.acname)); + + for (i = 0; i < g_wtp.radios->count; i++) { + struct wtp_radio* radio = (struct wtp_radio*)capwap_array_get_item_pointer(g_wtp.radios, i); + struct capwap_radioadmstate_element radioadmstate; + + radioadmstate.radioid = (unsigned char)(i + 1); + radioadmstate.state = ((radio->status == WTP_RADIO_DISABLED) ? CAPWAP_RADIO_ADMIN_STATE_DISABLED : CAPWAP_RADIO_ADMIN_STATE_ENABLED); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_RADIOADMSTATE_ELEMENT(&radioadmstate)); + } + + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_STATISTICSTIMER_ELEMENT(&g_wtp.statisticstimer)); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_WTPREBOOTSTAT_ELEMENT(&g_wtp.rebootstat)); + + /* CAPWAP_CREATE_ACNAMEPRIORITY_ELEMENT */ /* TODO */ + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_TRANSPORT_ELEMENT(&g_wtp.transport)); + /* CAPWAP_CREATE_WTPSTATICIPADDRESS_ELEMENT */ /* TODO */ + /* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */ + + /* Create Configuration Status request packet */ + if (!capwap_build_packet_validate(buildpacket, NULL)) { + wtp_free_reference_last_request(); + result = capwap_fragment_build_packet(buildpacket, g_wtp.requestfragmentpacket, g_wtp.mtu, g_wtp.fragmentid); + if (result == 1) { + g_wtp.fragmentid++; + } + } else { + capwap_logging_debug("Warning: build invalid configuretion status request packet"); + } + + capwap_build_packet_free(buildpacket); + + /* Send Configuration Status request to AC */ + if (result >= 0) { + for (i = 0; i < g_wtp.requestfragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(g_wtp.requestfragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&g_wtp.ctrldtls, g_wtp.acctrlsock.socket[g_wtp.acctrlsock.type], txpacket->header, txpacket->packetsize, &g_wtp.wtpctrladdress, &g_wtp.acctrladdress)) { + capwap_logging_debug("Warning: error to send configuration status request packet"); + result = -1; + break; + } + } + + if (result == -1) { + /* Error to send packets */ + wtp_free_reference_last_request(); + wtp_dfa_change_state(CAPWAP_CONFIGURE_TO_DTLS_TEARDOWN_STATE); + } else { + g_wtp.dfa.rfcRetransmitCount = 0; + capwap_set_timeout(g_wtp.dfa.rfcRetransmitInterval, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + wtp_dfa_change_state(CAPWAP_CONFIGURE_STATE); + status = WTP_DFA_ACCEPT_PACKET; + } + } else { + wtp_dfa_change_state(CAPWAP_CONFIGURE_TO_DTLS_TEARDOWN_STATE); + } + + return status; +} + +/* */ +int wtp_dfa_state_join_to_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(packet == NULL); + ASSERT(timeout != NULL); + + return wtp_teardown_connection(timeout); +} diff --git a/src/wtp/wtp_dfa_reset.c b/src/wtp/wtp_dfa_reset.c new file mode 100644 index 0000000..0da283b --- /dev/null +++ b/src/wtp/wtp_dfa_reset.c @@ -0,0 +1,11 @@ +#include "wtp.h" +#include "capwap_dfa.h" +#include "wtp_dfa.h" + +/* */ +int wtp_dfa_state_reset(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(packet == NULL); + ASSERT(timeout != NULL); + + return wtp_teardown_connection(timeout); +} diff --git a/src/wtp/wtp_dfa_run.c b/src/wtp/wtp_dfa_run.c new file mode 100644 index 0000000..567bbdf --- /dev/null +++ b/src/wtp/wtp_dfa_run.c @@ -0,0 +1,330 @@ +#include "wtp.h" +#include "capwap_dfa.h" +#include "capwap_element.h" +#include "wtp_dfa.h" + +/* */ +static int send_echo_request() { + int i; + int result = -1; + struct capwap_build_packet* buildpacket; + + /* Build packet */ + buildpacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, g_wtp.binding); + buildpacket->isctrlmsg = 1; + + /* Prepare echo request */ + capwap_build_packet_set_control_message_type(buildpacket, CAPWAP_ECHO_REQUEST, g_wtp.localseqnumber++); + /* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */ + + /* Create echo request packet */ + if (!capwap_build_packet_validate(buildpacket, NULL)) { + wtp_free_reference_last_request(); + result = capwap_fragment_build_packet(buildpacket, g_wtp.requestfragmentpacket, g_wtp.mtu, g_wtp.fragmentid); + if (result == 1) { + g_wtp.fragmentid++; + } + } else { + capwap_logging_debug("Warning: build invalid echo request packet"); + } + + capwap_build_packet_free(buildpacket); + + /* Send echo request to AC */ + if (result >= 0) { + for (i = 0; i < g_wtp.requestfragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(g_wtp.requestfragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&g_wtp.ctrldtls, g_wtp.acctrlsock.socket[g_wtp.acctrlsock.type], txpacket->header, txpacket->packetsize, &g_wtp.wtpctrladdress, &g_wtp.acctrladdress)) { + capwap_logging_debug("Warning: error to send echo request packet"); + result = -1; + break; + } + } + + if (result == -1) { + wtp_free_reference_last_request(); /* Error to send packets */ + } + } + + return result; +} + +/* */ +static int receive_echo_response(struct capwap_build_packet* buildpacket) { + unsigned short binding; + struct capwap_element_echo_response echoresponse; + + ASSERT(buildpacket != NULL); + + /* Valid packet, free request packet */ + wtp_free_reference_last_request(); + + /* Echo response info */ + binding = GET_WBID_HEADER(&buildpacket->header); + capwap_init_element_echo_response(&echoresponse, binding); + + /* Parsing elements list */ + if (capwap_parsing_element_echo_response(&echoresponse, buildpacket->elementslist->first)) { + /* TODO */ + } + + /* Free join response */ + capwap_free_element_echo_response(&echoresponse, binding); + return 0; +} + +/* */ +static void receive_reset_request(struct capwap_build_packet* buildpacket, struct capwap_packet* packet) { + unsigned long i; + unsigned short binding; + + ASSERT(buildpacket != NULL); + + /* */ + binding = GET_WBID_HEADER(&buildpacket->header); + if ((binding == g_wtp.binding) && IS_SEQUENCE_SMALLER(g_wtp.remoteseqnumber, buildpacket->ctrlmsg.seq)) { + struct capwap_element_reset_request resetrequest; + + /* Reset request info*/ + capwap_init_element_reset_request(&resetrequest, binding); + + /* Parsing elements list */ + if (capwap_parsing_element_reset_request(&resetrequest, buildpacket->elementslist->first)) { + struct capwap_build_packet* responsepacket; + struct capwap_resultcode_element resultcode = { CAPWAP_RESULTCODE_SUCCESS }; + + /* Create response */ + responsepacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, binding); + responsepacket->isctrlmsg = 1; + + /* Prepare echo response */ + capwap_build_packet_set_control_message_type(responsepacket, CAPWAP_RESET_RESPONSE, buildpacket->ctrlmsg.seq); + capwap_build_packet_add_message_element(responsepacket, CAPWAP_CREATE_RESULTCODE_ELEMENT(&resultcode)); + /* CAPWAP_CREATE_VENDORSPECIFICPAYLOAD_ELEMENT */ /* TODO */ + + if (!capwap_build_packet_validate(responsepacket, NULL)) { + int result; + + wtp_free_reference_last_response(); + + /* Send reset response to AC */ + result = capwap_fragment_build_packet(responsepacket, g_wtp.responsefragmentpacket, g_wtp.mtu, g_wtp.fragmentid); + if (result >= 0) { + if (result == 1) { + g_wtp.fragmentid++; + } + + /* Save remote sequence number */ + g_wtp.remoteseqnumber = buildpacket->ctrlmsg.seq; + capwap_get_packet_digest((void*)packet->header, packet->packetsize, g_wtp.lastrecvpackethash); + + /* Send */ + for (i = 0; i < g_wtp.responsefragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(g_wtp.responsefragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&g_wtp.ctrldtls, g_wtp.acctrlsock.socket[g_wtp.acctrlsock.type], txpacket->header, txpacket->packetsize, &g_wtp.wtpctrladdress, &g_wtp.acctrladdress)) { + /* Response is already created and saved. When receive a re-request, DFA autoresponse */ + capwap_logging_debug("Warning: error to send reset response packet"); + break; + } + } + } + } + + /* Free memory */ + capwap_build_packet_free(responsepacket); + } + + /* Free */ + capwap_free_element_reset_request(&resetrequest, binding); + } +} + +/* */ +int wtp_dfa_state_run(struct capwap_packet* packet, struct timeout_control* timeout) { + int status = WTP_DFA_ACCEPT_PACKET; + + ASSERT(timeout != NULL); + + if (packet) { + struct capwap_build_packet* buildpacket; + + buildpacket = capwap_rx_packet_create((void*)packet->header, packet->packetsize, packet->socket.isctrlsocket); + if (buildpacket) { + if (!capwap_build_packet_validate(buildpacket, NULL)) { + if (packet->socket.isctrlsocket) { + unsigned long typemsg = ntohl(buildpacket->ctrlmsg.type); + + if (capwap_is_request_type(typemsg) || ((g_wtp.localseqnumber - 1) == buildpacket->ctrlmsg.seq)) { + switch (typemsg) { + case CAPWAP_CONFIGURATION_UPDATE_RESPONSE: { + /* TODO */ + break; + } + + case CAPWAP_CHANGE_STATE_EVENT_REQUEST: { + /* TODO */ + break; + } + + case CAPWAP_ECHO_RESPONSE: { + if (!receive_echo_response(buildpacket)) { + capwap_kill_timeout(timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + capwap_set_timeout(g_wtp.dfa.rfcEchoInterval, timeout, CAPWAP_TIMER_CONTROL_ECHO); + } + + break; + } + + case CAPWAP_CLEAR_CONFIGURATION_RESPONSE: { + /* TODO */ + break; + } + + case CAPWAP_WTP_EVENT_REQUEST: { + /* TODO */ + break; + } + + case CAPWAP_DATA_TRANSFER_REQUEST: { + /* TODO */ + break; + } + + case CAPWAP_DATA_TRANSFER_RESPONSE: { + /* TODO */ + break; + } + + case CAPWAP_RESET_REQUEST: { + receive_reset_request(buildpacket, packet); + wtp_dfa_change_state(CAPWAP_RESET_STATE); + status = WTP_DFA_NO_PACKET; + break; + } + } + } + } else { + if (IS_FLAG_K_HEADER(&buildpacket->header) && capwap_is_enable_timeout(timeout, CAPWAP_TIMER_DATA_KEEPALIVEDEAD)) { + struct capwap_sessionid_element sessionid; + + if (capwap_get_sessionid_from_keepalive(buildpacket, &sessionid)) { + if (!memcmp(&sessionid, &g_wtp.sessionid, sizeof(struct capwap_sessionid_element))) { + /* Receive Data Keep-Alive, wait for next packet */ + capwap_kill_timeout(timeout, CAPWAP_TIMER_DATA_KEEPALIVEDEAD); + capwap_set_timeout(g_wtp.dfa.rfcDataChannelKeepAlive, timeout, CAPWAP_TIMER_DATA_KEEPALIVE); + } + } + } else { + /* TODO */ + + /* Update data keep-alive timeout */ + if (!capwap_is_enable_timeout(timeout, CAPWAP_TIMER_DATA_KEEPALIVEDEAD)) { + capwap_set_timeout(g_wtp.dfa.rfcDataChannelKeepAlive, timeout, CAPWAP_TIMER_DATA_KEEPALIVE); + } + } + } + } + + /* Free */ + capwap_build_packet_free(buildpacket); + } + } else { + if (capwap_is_timeout(timeout, CAPWAP_TIMER_CONTROL_CONNECTION)) { + int i; + + /* No response received */ + g_wtp.dfa.rfcRetransmitCount++; + if (g_wtp.dfa.rfcRetransmitCount >= g_wtp.dfa.rfcMaxRetransmit) { + /* Timeout run state */ + wtp_free_reference_last_request(); + wtp_dfa_change_state(CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE); + status = WTP_DFA_NO_PACKET; + } else { + /* Retransmit request */ + for (i = 0; i < g_wtp.requestfragmentpacket->count; i++) { + struct capwap_packet* txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(g_wtp.requestfragmentpacket, i); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&g_wtp.ctrldtls, g_wtp.acctrlsock.socket[g_wtp.acctrlsock.type], txpacket->header, txpacket->packetsize, &g_wtp.wtpctrladdress, &g_wtp.acctrladdress)) { + capwap_logging_debug("Warning: error to send request packet"); + break; + } + } + + /* Update timeout */ + capwap_set_timeout(g_wtp.dfa.rfcRetransmitInterval, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } + } else if (capwap_is_timeout(timeout, CAPWAP_TIMER_CONTROL_ECHO)) { + /* Disable echo timer */ + capwap_kill_timeout(timeout, CAPWAP_TIMER_CONTROL_ECHO); + + if (!send_echo_request()) { + g_wtp.dfa.rfcRetransmitCount = 0; + capwap_set_timeout(g_wtp.dfa.rfcRetransmitInterval, timeout, CAPWAP_TIMER_CONTROL_CONNECTION); + } else { + wtp_dfa_change_state(CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE); + status = WTP_DFA_NO_PACKET; + } + } else if (capwap_is_timeout(timeout, CAPWAP_TIMER_DATA_KEEPALIVE)) { + int result; + struct capwap_build_packet* buildpacket; + capwap_fragment_packet_array* txfragpacket; + + /* Build packet Data Keep-Alive*/ + buildpacket = capwap_tx_packet_create(CAPWAP_RADIOID_NONE, CAPWAP_WIRELESS_BINDING_NONE); + buildpacket->isctrlmsg = 0; + + /* */ + SET_FLAG_K_HEADER(&buildpacket->header, 1); + capwap_build_packet_add_message_element(buildpacket, CAPWAP_CREATE_SESSIONID_ELEMENT(&g_wtp.sessionid)); + + txfragpacket = capwap_array_create(sizeof(struct capwap_packet), 0); + result = capwap_fragment_build_packet(buildpacket, txfragpacket, CAPWAP_DONT_FRAGMENT, 0); + if (!result) { + struct capwap_packet* txpacket; + + ASSERT(txfragpacket->count == 1); + + txpacket = (struct capwap_packet*)capwap_array_get_item_pointer(txfragpacket, 0); + ASSERT(txpacket != NULL); + + if (!capwap_crypt_sendto(&g_wtp.datadtls, g_wtp.acdatasock.socket[g_wtp.acdatasock.type], txpacket->header, txpacket->packetsize, &g_wtp.wtpdataaddress, &g_wtp.acdataaddress)) { + capwap_logging_debug("Warning: error to send data channel keepalive packet"); + result = -1; + } + } + + capwap_fragment_free(txfragpacket); + capwap_array_free(txfragpacket); + capwap_build_packet_free(buildpacket); + + /* Send Configuration Status request to AC */ + if (!result) { + capwap_kill_timeout(timeout, CAPWAP_TIMER_DATA_KEEPALIVE); + capwap_set_timeout(g_wtp.dfa.rfcDataChannelDeadInterval, timeout, CAPWAP_TIMER_DATA_KEEPALIVEDEAD); + } else { + wtp_dfa_change_state(CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE); + status = WTP_DFA_NO_PACKET; + } + } else if (capwap_is_timeout(timeout, CAPWAP_TIMER_DATA_KEEPALIVEDEAD)) { + /* Data Keep-Alive timeout */ + capwap_kill_timeout(timeout, CAPWAP_TIMER_DATA_KEEPALIVEDEAD); + wtp_dfa_change_state(CAPWAP_RUN_TO_DTLS_TEARDOWN_STATE); + status = WTP_DFA_NO_PACKET; + } + } + + return status; +} + +/* */ +int wtp_dfa_state_run_to_dtlsteardown(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(packet == NULL); + ASSERT(timeout != NULL); + + return wtp_teardown_connection(timeout); +} diff --git a/src/wtp/wtp_dfa_sulking.c b/src/wtp/wtp_dfa_sulking.c new file mode 100644 index 0000000..a558613 --- /dev/null +++ b/src/wtp/wtp_dfa_sulking.c @@ -0,0 +1,27 @@ +#include "wtp.h" +#include "capwap_dfa.h" +#include "wtp_dfa.h" + +/* */ +int wtp_dfa_state_sulking(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + wtp_dfa_change_state(CAPWAP_SULKING_TO_IDLE_STATE); + + return WTP_DFA_NO_PACKET; +} + +/* */ +int wtp_dfa_state_sulking_to_idle(struct capwap_packet* packet, struct timeout_control* timeout) { + ASSERT(timeout != NULL); + ASSERT(packet == NULL); + + g_wtp.dfa.rfcDiscoveryCount = 0; + g_wtp.dfa.rfcFailedDTLSSessionCount = 0; + g_wtp.dfa.rfcFailedDTLSAuthFailCount = 0; + + wtp_dfa_change_state(CAPWAP_IDLE_STATE); + + return WTP_DFA_NO_PACKET; +} diff --git a/version.m4 b/version.m4 new file mode 100644 index 0000000..1bb973c --- /dev/null +++ b/version.m4 @@ -0,0 +1,5 @@ +dnl define the SmartCAPWAP version +define([PRODUCT_NAME], [SmartCAPWAP]) +define([PRODUCT_TARNAME], [smartcapwap]) +define([PRODUCT_VERSION], [1.0.0]) +define([PRODUCT_BUGREPORT], [vemax78@gmail.com])