Реализация связки Dlink DGS-3610-26G + DES-1228G(vlan на клиента) на основе схемы Cisco2 + Zyxel ( DGS-3610-26G + DES-1228G )
Материал из BiTel WiKi
Stark (Обсуждение | вклад) |
Stark (Обсуждение | вклад) м (переименовал «Пример 2. реализации шлюза Cisco2( DGS-3610-26G) на BeanShell c управлением telnet» в «[[Реализация связки Dlink DGS-3610-26G + DES-1228G(vlan на клиента) на о) |
||
(3 промежуточные версии не показаны) | |||
Строка 1: | Строка 1: | ||
+ | Подключение абонентов по схеме "vlan-на-клиента", Это замена стандартной схему Cisco2 + Zyxel ( DGS-3610-26G + DES-1228G ). | ||
+ | При этом используется оборудование D-Link - на уровне доступа свитчи DES-1228G, в ядре - DGS-3610-26G. | ||
+ | И еще в отличи от стандартной схемы порты берутся ни из редактора шлюза Zyxel, а универсальный со вкладки порты в договоре . | ||
+ | |||
+ | |||
+ | Управление осуществляется по telnet. К сожалению в используемом | ||
+ | оборудовании функционал "ip unnumbered" реализован не так как в | ||
+ | коммутаторах Cisco, поэтому использовать его не получится. Работать | ||
+ | необходимо используя технологию supervlan. В кратце - эта технология | ||
+ | позволяет задавать допустимые IP-адреса для конкретного vlan, при этом | ||
+ | закрепить можно только диапазон состоящий минимум из 2-х адресов. Например: | ||
+ | DGS-3610# configure | ||
+ | DGS-3610(config)# vlan 1000 | ||
+ | DGS-3610(config-vlan)# subvlan-address-range 192.168.100.10 192.168.100.11 | ||
+ | DGS-3610(config-vlan)# end | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | конфигурация шлюза DGS-3610-26G | ||
+ | |||
+ | <source lang="bash"> | ||
+ | user_rule.editor.class=bitel.billing.module.services.ipn.editor.vlan.CiscoVlanContactRuleEditor | ||
+ | gate_manager.class=bitel.billing.server.ipn.vlan.CiscoVlanGateWorker | ||
+ | use.script=1 | ||
+ | </source> | ||
+ | |||
+ | Команды шлюза DGS-3610-26G | ||
+ | |||
+ | <source lang="bash"> | ||
+ | [DEFAULT] | ||
+ | |||
+ | [REMOVE] | ||
+ | no vlan {VID} | ||
+ | [/REMOVE] | ||
+ | |||
+ | [OPEN] | ||
+ | |||
+ | vlan {VID} | ||
+ | exit | ||
+ | vlan 100 | ||
+ | subvlan {VID} | ||
+ | exit | ||
+ | vlan {VID} | ||
+ | subvlan-address-range {addr1} {addr2} | ||
+ | exit | ||
+ | |||
+ | ip access-list extended 101 | ||
+ | <LOOP> | ||
+ | no deny ip host {A} any | ||
+ | no deny ip any host {A} | ||
+ | </LOOP> | ||
+ | exit | ||
+ | [/OPEN] | ||
+ | |||
+ | [CLOSE] | ||
+ | interface gigabitethernet 0/22 | ||
+ | no ip access-group 101 in | ||
+ | exit | ||
+ | ip access-list extended 101 | ||
+ | no permit ip any any | ||
+ | <LOOP> | ||
+ | deny ip host {A} any | ||
+ | deny ip any host {A} | ||
+ | </LOOP> | ||
+ | permit ip any any | ||
+ | exit | ||
+ | interface gigabitethernet 0/22 | ||
+ | ip access-group 101 in | ||
+ | exit | ||
+ | [/CLOSE] | ||
+ | [/DEFAULT] | ||
+ | |||
+ | </source> | ||
+ | |||
+ | скрпит шлюза DGS-3610-26G | ||
<source lang="java"> | <source lang="java"> | ||
import java.io.IOException; | import java.io.IOException; | ||
Строка 15: | Строка 92: | ||
import bitel.billing.server.util.DefaultServerSetup; | import bitel.billing.server.util.DefaultServerSetup; | ||
import bitel.billing.server.util.Utils; | import bitel.billing.server.util.Utils; | ||
+ | import bitel.billing.server.util.telnet.OperationTimedoutException; | ||
import bitel.billing.server.util.telnet.TelnetSession; | import bitel.billing.server.util.telnet.TelnetSession; | ||
import ru.bitel.bgbilling.common.DefaultSetup; | import ru.bitel.bgbilling.common.DefaultSetup; | ||
import bitel.billing.common.IPUtils; | import bitel.billing.common.IPUtils; | ||
import bitel.billing.server.util.Utils; | import bitel.billing.server.util.Utils; | ||
+ | import bitel.billing.server.util.telnet.OperationTimedoutException; | ||
import bitel.billing.server.util.telnet.TelnetSession; | import bitel.billing.server.util.telnet.TelnetSession; | ||
- | + | import ru.bitel.bgbilling.modules.ipn.common.bean.*; | |
+ | import bitel.billing.server.util.telnet.ansi.TelnetAnsiSession; | ||
//includeBGBS( "bgbs://ru.bitel.bgbilling.kernel.script.common.bean.ScriptLibrary/manad" ); | //includeBGBS( "bgbs://ru.bitel.bgbilling.kernel.script.common.bean.ScriptLibrary/manad" ); | ||
Строка 27: | Строка 107: | ||
{ | { | ||
log.info( "start of cisco........................................................"); | log.info( "start of cisco........................................................"); | ||
+ | |||
host = gate.getHost(); | host = gate.getHost(); | ||
port = gate.getPort(); | port = gate.getPort(); | ||
Строка 47: | Строка 128: | ||
log.debug("before connect"); | log.debug("before connect"); | ||
- | session = new | + | session = new TelnetAnsiSession( host, port); |
session.setTimeout( timeout ); | session.setTimeout( timeout ); | ||
session.setLoginPromptSequence( ":" ); | session.setLoginPromptSequence( ":" ); | ||
- | session.connect(); | + | result.append( session.connect() ); |
log.debug("after connect"); | log.debug("after connect"); | ||
Строка 74: | Строка 155: | ||
- | log.info("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | + | log.info("!!!!!!!!!!!start of log!!!!!!!!!!!!!!!!!!!!!"); |
log.info( result ); | log.info( result ); | ||
- | log.info("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); | + | log.info("!!!!!!!!!!!!!!! end of log !!!!!!!!!!!!!!!!!"); |
log.debug( "ok" ); | log.debug( "ok" ); | ||
Строка 82: | Строка 163: | ||
log.info( "end of cisco........................................................"); | log.info( "end of cisco........................................................"); | ||
+ | |||
} | } | ||
Строка 90: | Строка 172: | ||
- | private void doCommands( | + | private void doCommands( session, result, gateSetup) throws IOException, OperationTimedoutException |
{ | { | ||
acl = gateSetup.getStringValue( "acl"); | acl = gateSetup.getStringValue( "acl"); | ||
- | vlanResult = getVlanResult( session ); | + | vlanResult = getVlanResult( session ); |
+ | log.info( "!!!!!!!!!!!!!!!!!!!!!!!!!!vlanResult!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" ); | ||
+ | log.info( vlanResult ); | ||
+ | log.info( "!!!!!!!!!!!!!!!!!!!!!!!!!!vlanResult!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" ); | ||
+ | |||
aclResult = getAclResult( session, acl ); | aclResult = getAclResult( session, acl ); | ||
Строка 105: | Строка 191: | ||
VlanManager manager = new VlanManager(status.mid, con); | VlanManager manager = new VlanManager(status.mid, con); | ||
gateId = gate.getId(); | gateId = gate.getId(); | ||
- | log.info("gateId=" + gateId); | + | //log.info("gateId=" + gateId); |
vid = manager.getVlan( gateId, status.contractId ); | vid = manager.getVlan( gateId, status.contractId ); | ||
- | log.info("vid=" + vid); | + | //log.info("vid=" + vid); |
+ | log.debug( "status=" + status.status); | ||
Строка 122: | Строка 209: | ||
addr1 = IPUtils.convertIpToString( range.getAddr1() ); | addr1 = IPUtils.convertIpToString( range.getAddr1() ); | ||
addr2 = IPUtils.convertIpToString( range.getAddr2() ); | addr2 = IPUtils.convertIpToString( range.getAddr2() ); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | log.info( "empty adress on cid = " + status.contractId ); | ||
+ | continue; | ||
} | } | ||
Строка 130: | Строка 222: | ||
rules = getOpenRules( status, vid, gateSetup, addr1, addr2, acl ); | rules = getOpenRules( status, vid, gateSetup, addr1, addr2, acl ); | ||
} | } | ||
- | else if (status.status == IPNContractStatus.STATUS_REMOVED && vlanExists(vid, vlanResult) ) | + | else if (status.status == IPNContractStatus.STATUS_REMOVED && ( vlanExists(vid, vlanResult) || isUserDenied( addr1, addr2,aclResult) ) ) |
{ | { | ||
rules = getRemoveRules( status, vid, gateSetup, addr1, addr2, acl ); | rules = getRemoveRules( status, vid, gateSetup, addr1, addr2, acl ); | ||
} | } | ||
//if closed and etc | //if closed and etc | ||
- | else if ( ! | + | else if ( status.status != IPNContractStatus.STATUS_OPEN && status.status != IPNContractStatus.STATUS_REMOVED && !isUserDenied( addr1, addr2, aclResult) ) |
{ | { | ||
rules = getCloseRules( status, vid, gateSetup, addr1, addr2, acl ); | rules = getCloseRules( status, vid, gateSetup, addr1, addr2, acl ); | ||
Строка 144: | Строка 236: | ||
for ( String rule : rules ) | for ( String rule : rules ) | ||
{ | { | ||
- | log.debug ("command=" + rule ); | + | //log.debug ("command=" + rule ); |
result.append( session.doCommand( rule ) ); | result.append( session.doCommand( rule ) ); | ||
} | } | ||
Строка 155: | Строка 247: | ||
getOpenRules( status, vid, gateSetup, addr1, addr2, acl ) | getOpenRules( status, vid, gateSetup, addr1, addr2, acl ) | ||
{ | { | ||
- | + | log.debug( "geting open rules..."); | |
+ | return getRules( status, "\\[OPEN\\](.*)\\[/OPEN\\]", vid, gateSetup, addr1, addr2, acl ); | ||
} | } | ||
getCloseRules( status, vid, gateSetup, addr1, addr2, acl ) | getCloseRules( status, vid, gateSetup, addr1, addr2, acl ) | ||
{ | { | ||
- | + | log.debug( "geting close rules..."); | |
+ | return getRules( status, "\\[CLOSE\\](.*)\\[/CLOSE\\]", vid, gateSetup, addr1, addr2, acl ); | ||
} | } | ||
getRemoveRules( UserStatus status, vid, gateSetup, addr1, addr2, acl ) | getRemoveRules( UserStatus status, vid, gateSetup, addr1, addr2, acl ) | ||
- | { | + | { |
- | + | log.debug( "geting remove rules..."); | |
+ | return getRules( status, "\\[REMOVE\\](.*)\\[/REMOVE\\]", vid, gateSetup, addr1, addr2, acl ); | ||
} | } | ||
Строка 171: | Строка 266: | ||
{ | { | ||
// пользовательское правило, без типа - то все оставляем как есть | // пользовательское правило, без типа - то все оставляем как есть | ||
- | + | CiscoRule ciscoRule = new CiscoRule ( status.rule.getRuleText() ); | |
- | log.info("rule=" + rule); | + | rule = ciscoRule.getAddresses(); |
+ | |||
+ | //log.info("rule=" + rule); | ||
//типизированное правило | //типизированное правило | ||
Строка 181: | Строка 278: | ||
} | } | ||
- | log.info("rule=" + rule); | + | //log.info("rule=" + rule); |
Строка 234: | Строка 331: | ||
{ | { | ||
//эскейпируем точки в адресе | //эскейпируем точки в адресе | ||
- | String address = addr1.replace( ".", "\\." ); | + | String address1 = addr1.replace( ".", "\\." ); |
- | log.debug( "aaddr1=" + address ); | + | String address2 = addr2.replace( ".", "\\." ); |
+ | |||
+ | return isUserDeniedForAddress( address1, buffer ) && isUserDeniedForAddress( address2, buffer ); | ||
+ | } | ||
+ | |||
+ | private isUserDeniedForAddress ( address, buffer) | ||
+ | { | ||
+ | //эскейпируем точки в адресе | ||
+ | //String address = addr1.replace( ".", "\\." ); | ||
+ | //log.debug( "aaddr1=" + address ); | ||
String template = ".*deny ip host\\s+" + address + "\\s+any.*$"; | String template = ".*deny ip host\\s+" + address + "\\s+any.*$"; | ||
- | log.debug( "template=" + template ); | + | //log.debug( "template=" + template ); |
Pattern pattern = Pattern.compile( template, Pattern.DOTALL ); | Pattern pattern = Pattern.compile( template, Pattern.DOTALL ); | ||
Matcher m = pattern.matcher( buffer ); | Matcher m = pattern.matcher( buffer ); | ||
Строка 258: | Строка 364: | ||
} | } | ||
+ | |||
private vlanExists ( vlan, buffer) | private vlanExists ( vlan, buffer) | ||
Строка 279: | Строка 386: | ||
return session.doCommand( "show access-lists " + acl ); | return session.doCommand( "show access-lists " + acl ); | ||
} | } | ||
+ | |||
+ | </source> | ||
+ | |||
+ | конфигурация шлюза Доступа Dlink | ||
+ | |||
+ | <source lang="bash"> | ||
+ | user_rule.editor.class=bitel.billing.module.services.ipn.editor.vlan.CiscoSSHSwitchRuleEditor | ||
+ | gate_manager.class=bitel.billing.server.ipn.vlan.CiscoSSHSwitchGateWorker | ||
+ | use.script=1 | ||
+ | </source> | ||
+ | |||
+ | Команды шлюза доступа | ||
+ | <source lang="bash"> | ||
+ | [DEFAULT] | ||
+ | |||
+ | [OPEN] | ||
+ | create vlan vlan{VID} tag {VID} | ||
+ | config vlan vlan{VID} add tagged {UPLINK_PORT} | ||
+ | |||
+ | <LOOP_PORT> | ||
+ | config vlan vlan{VID} add untagged {PORT} | ||
+ | config ports {PORT} description {CONTRACT_NAME} | ||
+ | </LOOP_PORT> | ||
+ | save config | ||
+ | [/OPEN] | ||
+ | |||
+ | [CLOSE] | ||
+ | [/CLOSE] | ||
+ | |||
+ | [REMOVE] | ||
+ | delete vlan vlan{VID} | ||
+ | <LOOP_PORT> | ||
+ | config ports {PORT} clear_description | ||
+ | </LOOP_PORT> | ||
+ | save config | ||
+ | [/REMOVE] | ||
+ | |||
+ | [/DEFAULT] | ||
+ | |||
+ | </source> | ||
+ | |||
+ | Скрипт шлюза доступа | ||
+ | <source lang="bash"> | ||
+ | import java.io.IOException; | ||
+ | import java.util.ArrayList; | ||
+ | import java.util.HashMap; | ||
+ | import java.util.List; | ||
+ | import java.util.Map; | ||
+ | import java.util.StringTokenizer; | ||
+ | import java.util.regex.Matcher; | ||
+ | import java.util.regex.Pattern; | ||
+ | |||
+ | import bitel.billing.common.IPUtils; | ||
+ | import bitel.billing.common.module.ipn.IPNContractStatus; | ||
+ | import bitel.billing.server.ipn.UserStatus; | ||
+ | import bitel.billing.server.ipn.dlink.UserPortStatus; | ||
+ | import bitel.billing.server.util.DefaultServerSetup; | ||
+ | import bitel.billing.server.util.Utils; | ||
+ | import bitel.billing.server.util.telnet.OperationTimedoutException; | ||
+ | import bitel.billing.server.util.telnet.TelnetSession; | ||
+ | import bitel.billing.server.ipn.bean.*; | ||
+ | import bitel.billing.server.contract.bean.*; | ||
+ | import bitel.billing.server.util.telnet.ansi.TelnetAnsiSession; | ||
+ | import ru.bitel.bgbilling.modules.ipn.server.bean.GatePortManager; | ||
+ | |||
+ | protected void doSync() | ||
+ | { | ||
+ | String host = gate.getHost(); | ||
+ | int port = gate.getPort(); | ||
+ | |||
+ | |||
+ | DefaultServerSetup gateSetup = new DefaultServerSetup( gate.getConfig(), "\r\n" ); | ||
+ | |||
+ | |||
+ | String pswd = gate.getKeyword(); | ||
+ | String login = gateSetup.getStringValue( "login"); | ||
+ | |||
+ | |||
+ | |||
+ | StringBuffer result = new StringBuffer(); | ||
+ | |||
+ | |||
+ | if( log.isDebugEnabled() ) | ||
+ | { | ||
+ | log.debug( gate.getId() + " gate: " + host + ":" + port + " login: " + login + " pswd: " + pswd ); | ||
+ | } | ||
+ | |||
+ | |||
+ | TelnetAnsiSession session = new TelnetAnsiSession( host, port); | ||
+ | session.setLoginPromptSequence( "UserName:" ); | ||
+ | |||
+ | log.debug( "before connect" ); | ||
+ | result.append( session.connect() ); | ||
+ | log.debug( "after connect" ); | ||
+ | |||
+ | session.setLoginPromptSequence( ":" ); | ||
+ | result.append( session.doCommand( login ) ); | ||
+ | log.debug( "after command" ); | ||
+ | |||
+ | |||
+ | session.setLoginPromptSequence( "#" ); | ||
+ | result.append( session.doCommand( pswd ) ); | ||
+ | log.debug( "before pswd" ); | ||
+ | |||
+ | log.debug( "execute commands" ); | ||
+ | doCommands( session, result, gateSetup); | ||
+ | |||
+ | |||
+ | |||
+ | session.setLoginPromptSequence( "****" ); | ||
+ | result.append( session.doCommand( "logout" ) ); | ||
+ | |||
+ | |||
+ | log.info( "!!!!!!!!!!!!!!!!!!!!!!!!!!! Dlink 3028 !!!!!!!!!!!!!!!!!!!!!!!!!!!!" ); | ||
+ | log.info( result.toString() ); | ||
+ | log.info( "!!!!!!!!!!!!!!!!!!!!!!!!!!! end of Dlink 3028 !!!!!!!!!!!!!!!!!!!!!!!!!!!!" ); | ||
+ | |||
+ | |||
+ | |||
+ | log.debug( "ok" ); | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | private void doCommands( session, result, DefaultServerSetup gateSetup ) | ||
+ | throws IOException, OperationTimedoutException | ||
+ | { | ||
+ | |||
+ | VlanManager manager = new VlanManager(mid, con); | ||
+ | ContractManager cm = new ContractManager( con ); | ||
+ | |||
+ | StringBuilder resultCheck = new StringBuilder(); | ||
+ | |||
+ | session.setLoginPromptSequence( "All" ); | ||
+ | resultCheck.append( session.doCommand("show vlan ports 1-28" ) ); | ||
+ | session.setLoginPromptSequence( "#" ); | ||
+ | resultCheck.append( session.doCommand("all" ) ); | ||
+ | |||
+ | |||
+ | Map vlanPortMap = readPortVlanMap( resultCheck.toString() ); | ||
+ | log.debug("!!!!!!!!vlan-port check!!!!!!!!!!!!"); | ||
+ | for ( entry : vlanPortMap.entrySet() ) | ||
+ | { | ||
+ | log.debug("port " + entry.getKey() ); | ||
+ | for ( s : entry.getValue() ) | ||
+ | { | ||
+ | log.debug("vlan " + s ); | ||
+ | } | ||
+ | } | ||
+ | log.debug( "!!!!!!!!end vlan-port check!!!!!!!!!!!!" ); | ||
+ | |||
+ | |||
+ | |||
+ | for( UserStatus status : statusList ) | ||
+ | { | ||
+ | |||
+ | String addrs = null; | ||
+ | int port = -1 ; | ||
+ | List ports = new GatePortManager( con, mid ).getGatePorts( status.contractId, gate.getId() ); | ||
+ | //типизированное правило | ||
+ | if( status.ruleType != null ) | ||
+ | { | ||
+ | rule = status.rule.getRuleText(); | ||
+ | |||
+ | addrs = getAddresses( rule ); | ||
+ | |||
+ | if ( ports.size() > 0 ) | ||
+ | { | ||
+ | port = ports.get(0); | ||
+ | log.debug( "port=" + port ); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //берем vlan у родительской циски | ||
+ | int vid = manager.getVlan( gate.getParentId(), status.contractId ); | ||
+ | //провереям только один порт | ||
+ | Set vlans = vlanPortMap.get( port ); | ||
+ | boolean exists = vlans != null && vlans.contains( vid ); | ||
+ | |||
+ | log.debug( "exists=" + exists ); | ||
+ | |||
+ | String[] rules = new String[0]; | ||
+ | |||
+ | Contract c = cm.getContractByID( status.contractId ); | ||
+ | String title = c.getTitle(); | ||
+ | |||
+ | if ( status.status == IPNContractStatus.STATUS_OPEN && !exists ) | ||
+ | { | ||
+ | rules = getOpenRules( status, vid, title, gateSetup, addrs, ports ); | ||
+ | |||
+ | } | ||
+ | else if ( status.status == IPNContractStatus.STATUS_REMOVED && exists ) | ||
+ | { | ||
+ | rules = getRemoveRules( status, vid, title, gateSetup, addrs, ports ); | ||
+ | } | ||
+ | //close and etc..пока плевать. т.к они все равно пустые | ||
+ | else | ||
+ | { | ||
+ | rules = getCloseRules( status, vid, title, gateSetup, addrs, ports ); | ||
+ | } | ||
+ | |||
+ | for ( String rule : rules ) | ||
+ | { | ||
+ | log.debug( "command=" + rule ); | ||
+ | result.append( session.doCommand( rule ) ); | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | } | ||
+ | |||
+ | private String[] getOpenRules( UserStatus status, int vid, title, gateSetup, addrs, ports ) | ||
+ | { | ||
+ | |||
+ | return getRules( status, "\\[OPEN\\](.*)\\[/OPEN\\]", vid, title, gateSetup, addrs, ports ); | ||
+ | } | ||
+ | |||
+ | private String[] getCloseRules( UserStatus status, int vid, title, gateSetup, addrs, ports ) | ||
+ | { | ||
+ | return getRules( status, "\\[CLOSE\\](.*)\\[/CLOSE\\]", vid, title, gateSetup, addrs, ports ); | ||
+ | } | ||
+ | |||
+ | private String[] getRemoveRules( UserStatus status, int vid, title, gateSetup, addrs, ports ) | ||
+ | { | ||
+ | return getRules( status, "\\[REMOVE\\](.*)\\[/REMOVE\\]", vid, title, gateSetup, addrs, ports); | ||
+ | } | ||
+ | |||
+ | getRules( status, template, vid, title, gateSetup, addrs, ports ) | ||
+ | { | ||
+ | // пользовательское правило, без типа - то все оставляем как есть | ||
+ | rule = status.rule.getRuleText(); | ||
+ | |||
+ | //типизированное правило | ||
+ | if( status.ruleType != null ) | ||
+ | { | ||
+ | rule = generateRule( addrs, ports, vid, status.gateType, status.ruleType, title, gateSetup ); | ||
+ | } | ||
+ | |||
+ | pattern = Pattern.compile( template, Pattern.DOTALL ); | ||
+ | m = pattern.matcher( rule ); | ||
+ | if (m.find()) | ||
+ | { | ||
+ | rule = m.group( 1 ); | ||
+ | } | ||
+ | |||
+ | rule.replaceAll( "\r", "" ); | ||
+ | parts = rule.split( "\n" ); | ||
+ | |||
+ | return parts; | ||
+ | } | ||
+ | |||
+ | generateRule( addreses, ports, vid, gateType, ruleType, title, gateSetup ) | ||
+ | { | ||
+ | ruleText = ManadUtils.getRule( gateType, ruleType ); | ||
+ | |||
+ | replacements = new HashMap(); | ||
+ | |||
+ | if ( vid > 0) | ||
+ | { | ||
+ | replacements.put( "\\{VID\\}", String.valueOf( vid ) ); | ||
+ | } | ||
+ | uplink = gateSetup.getIntValue( "uplink.port", 0 ); | ||
+ | replacements.put( "\\{UPLINK_PORT\\}", String.valueOf( uplink ) ); | ||
+ | replacements.put( "\\{CONTRACT_NAME\\}", title ); | ||
+ | |||
+ | loops = ManadUtils.getAddresLoops( addreses ); | ||
+ | |||
+ | String [] portArray = new String[ports.size()]; | ||
+ | for ( int i = 0; i < ports.size(); i++ ) | ||
+ | { | ||
+ | portArray[i] = String.valueOf( ports.get(i) ); | ||
+ | } | ||
+ | |||
+ | //адреса | ||
+ | p = new LoopPattern(); | ||
+ | p.setLoopPatern( "LOOP_PORT" ); | ||
+ | p.setReplace( "\\{PORT\\}" ); | ||
+ | p.setObjects( portArray ); | ||
+ | loops.add( p ); | ||
+ | |||
+ | |||
+ | |||
+ | return ManadUtils.generateRule( ruleText, replacements, ruleType, loops ); | ||
+ | } | ||
+ | |||
+ | |||
+ | String getAddresses( rule ) | ||
+ | { | ||
+ | Map result = new HashMap(); | ||
+ | |||
+ | //порты идут до #, а адреса идут после | ||
+ | String [] parts = rule.split( "#" ); | ||
+ | |||
+ | addresesStr = ""; | ||
+ | |||
+ | if ( parts.length > 1 ) | ||
+ | { | ||
+ | addresesStr = parts[1]; | ||
+ | } | ||
+ | |||
+ | /* | ||
+ | portsStr = ""; | ||
+ | if ( parts.length > 0 ) | ||
+ | { | ||
+ | portsStr = parts[0]; | ||
+ | } | ||
+ | |||
+ | |||
+ | portList = new ArrayList(); | ||
+ | parts = portsStr.split( ";" ); | ||
+ | |||
+ | for (String part : parts ) | ||
+ | { | ||
+ | parts2 = part.split( ":" ); | ||
+ | if ( parts2.length < 2) | ||
+ | { | ||
+ | continue; | ||
+ | } | ||
+ | port = parts2 [1]; | ||
+ | portList.add( port ); | ||
+ | } | ||
+ | |||
+ | String [] ports_ = new String [portList.size()]; | ||
+ | for (int i = 0; i < ports_.length; i++) | ||
+ | { | ||
+ | ports_[i] = portList.get( i ); | ||
+ | } | ||
+ | |||
+ | result.put( "ports", ports_); | ||
+ | result.put( "addrs", addresesStr); | ||
+ | */ | ||
+ | return addresesStr; | ||
+ | } | ||
+ | |||
+ | readPortVlanMap( String data ) | ||
+ | { | ||
+ | Map result = new HashMap(); | ||
+ | String [] lines = data.split( "\n" ); | ||
+ | |||
+ | String templatePort = ".*?Port\\s(\\d+).*?$"; | ||
+ | Pattern patternPort = Pattern.compile( templatePort, Pattern.DOTALL ); | ||
+ | Matcher m = patternPort.matcher( data ); | ||
+ | |||
+ | String templateVlan = ".*?(\\d+)\\s+X.*?$"; | ||
+ | Pattern patternVlan = Pattern.compile( templateVlan, Pattern.DOTALL ); | ||
+ | |||
+ | |||
+ | |||
+ | int port = 1; | ||
+ | |||
+ | |||
+ | for ( String line : lines ) | ||
+ | { | ||
+ | m = patternPort.matcher( line ); | ||
+ | if ( m.find() ) | ||
+ | { | ||
+ | port = Integer.valueOf( m.group( 1 ) ); | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | m = patternVlan.matcher( line ); | ||
+ | if ( m.find() ) | ||
+ | { | ||
+ | int vlan = Integer.valueOf( m.group( 1 ) ); | ||
+ | Set vlans = result.get( port ); | ||
+ | if ( vlans == null ) | ||
+ | { | ||
+ | vlans = new HashSet(); | ||
+ | result.put( port, vlans ); | ||
+ | } | ||
+ | vlans.add( vlan ); | ||
+ | } | ||
+ | |||
+ | //2 - X | ||
+ | |||
+ | |||
+ | } | ||
+ | return result; | ||
+ | } | ||
+ | |||
</source> | </source> |
Текущая версия на 06:43, 16 апреля 2010
Подключение абонентов по схеме "vlan-на-клиента", Это замена стандартной схему Cisco2 + Zyxel ( DGS-3610-26G + DES-1228G ). При этом используется оборудование D-Link - на уровне доступа свитчи DES-1228G, в ядре - DGS-3610-26G. И еще в отличи от стандартной схемы порты берутся ни из редактора шлюза Zyxel, а универсальный со вкладки порты в договоре .
Управление осуществляется по telnet. К сожалению в используемом
оборудовании функционал "ip unnumbered" реализован не так как в
коммутаторах Cisco, поэтому использовать его не получится. Работать
необходимо используя технологию supervlan. В кратце - эта технология
позволяет задавать допустимые IP-адреса для конкретного vlan, при этом
закрепить можно только диапазон состоящий минимум из 2-х адресов. Например:
DGS-3610# configure DGS-3610(config)# vlan 1000 DGS-3610(config-vlan)# subvlan-address-range 192.168.100.10 192.168.100.11 DGS-3610(config-vlan)# end
конфигурация шлюза DGS-3610-26G
user_rule.editor.class=bitel.billing.module.services.ipn.editor.vlan.CiscoVlanContactRuleEditor gate_manager.class=bitel.billing.server.ipn.vlan.CiscoVlanGateWorker use.script=1
Команды шлюза DGS-3610-26G
[DEFAULT] [REMOVE] no vlan {VID} [/REMOVE] [OPEN] vlan {VID} exit vlan 100 subvlan {VID} exit vlan {VID} subvlan-address-range {addr1} {addr2} exit ip access-list extended 101 <LOOP> no deny ip host {A} any no deny ip any host {A} </LOOP> exit [/OPEN] [CLOSE] interface gigabitethernet 0/22 no ip access-group 101 in exit ip access-list extended 101 no permit ip any any <LOOP> deny ip host {A} any deny ip any host {A} </LOOP> permit ip any any exit interface gigabitethernet 0/22 ip access-group 101 in exit [/CLOSE] [/DEFAULT]
скрпит шлюза DGS-3610-26G
import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.StringTokenizer; import bitel.billing.common.module.ipn.IPNContractStatus; import bitel.billing.server.ipn.GateWorker; import bitel.billing.server.ipn.UserStatus; import bitel.billing.server.ipn.bean.*; import bitel.billing.server.util.DefaultServerSetup; import bitel.billing.server.util.Utils; import bitel.billing.server.util.telnet.OperationTimedoutException; import bitel.billing.server.util.telnet.TelnetSession; import ru.bitel.bgbilling.common.DefaultSetup; import bitel.billing.common.IPUtils; import bitel.billing.server.util.Utils; import bitel.billing.server.util.telnet.OperationTimedoutException; import bitel.billing.server.util.telnet.TelnetSession; import ru.bitel.bgbilling.modules.ipn.common.bean.*; import bitel.billing.server.util.telnet.ansi.TelnetAnsiSession; //includeBGBS( "bgbs://ru.bitel.bgbilling.kernel.script.common.bean.ScriptLibrary/manad" ); protected void doSync() { log.info( "start of cisco........................................................"); host = gate.getHost(); port = gate.getPort(); DefaultServerSetup gateSetup = new DefaultServerSetup( gate.getConfig(), "\r\n" ); pswd = gate.getKeyword(); login = gateSetup.getStringValue( "login"); timeout = gateSetup.getIntValue( "timeout", 0 ); result = new StringBuffer(); if( log.isDebugEnabled() ) { log.debug( gate.getId() + " gate: " + host + ":" + port + " login: " + login + " passwd: " + pswd ); } log.debug("before connect"); session = new TelnetAnsiSession( host, port); session.setTimeout( timeout ); session.setLoginPromptSequence( ":" ); result.append( session.connect() ); log.debug("after connect"); result.append( session.doCommand( login ) ); log.debug("after login"); session.setLoginPromptSequence( "#" ); result.append( session.doCommand( pswd ) ); log.debug("after pswd"); result.append( session.doCommand( "terminal length 0" ) ); result.append( session.doCommand( "terminal width 0" ) ); log.debug("after terminal"); result.append( session.doCommand( "configure terminal" ) ); log.debug( "execute commands" ); doCommands( session, result, gateSetup ); result.append( session.doCommand( "exit" ) ); result.append( session.doCommand( "write memory" ) ); session.doCommandAsync( "exit" ); log.info("!!!!!!!!!!!start of log!!!!!!!!!!!!!!!!!!!!!"); log.info( result ); log.info("!!!!!!!!!!!!!!! end of log !!!!!!!!!!!!!!!!!"); log.debug( "ok" ); log.info( "end of cisco........................................................"); } protected void add() { } private void doCommands( session, result, gateSetup) throws IOException, OperationTimedoutException { acl = gateSetup.getStringValue( "acl"); vlanResult = getVlanResult( session ); log.info( "!!!!!!!!!!!!!!!!!!!!!!!!!!vlanResult!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" ); log.info( vlanResult ); log.info( "!!!!!!!!!!!!!!!!!!!!!!!!!!vlanResult!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" ); aclResult = getAclResult( session, acl ); log.info( "!!!!!!!!!!!!!!!!!!!!!!!!!!aclResult!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" ); log.info( aclResult ); log.info( "!!!!!!!!!!!!!!!!!!!!!!!!!! end aclResult!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" ); for( UserStatus status : statusList ) { VlanManager manager = new VlanManager(status.mid, con); gateId = gate.getId(); //log.info("gateId=" + gateId); vid = manager.getVlan( gateId, status.contractId ); //log.info("vid=" + vid); log.debug( "status=" + status.status); String addr1 = null; String addr2 = null; date = new GregorianCalendar(); AddressRangeManager man = new AddressRangeManager( con, mid ); addressList = man.getContractAddressRange( status.contractId , date, -1 ); if ( addressList.size() > 0 ) { AddressRange range = addressList.get(0); addr1 = IPUtils.convertIpToString( range.getAddr1() ); addr2 = IPUtils.convertIpToString( range.getAddr2() ); } else { log.info( "empty adress on cid = " + status.contractId ); continue; } rules = null; if (status.status == IPNContractStatus.STATUS_OPEN && (!vlanExists(vid, vlanResult) || isUserDenied( addr1, addr2, aclResult) ) ) { rules = getOpenRules( status, vid, gateSetup, addr1, addr2, acl ); } else if (status.status == IPNContractStatus.STATUS_REMOVED && ( vlanExists(vid, vlanResult) || isUserDenied( addr1, addr2,aclResult) ) ) { rules = getRemoveRules( status, vid, gateSetup, addr1, addr2, acl ); } //if closed and etc else if ( status.status != IPNContractStatus.STATUS_OPEN && status.status != IPNContractStatus.STATUS_REMOVED && !isUserDenied( addr1, addr2, aclResult) ) { rules = getCloseRules( status, vid, gateSetup, addr1, addr2, acl ); } if (vid > 0 && rules != null ) { for ( String rule : rules ) { //log.debug ("command=" + rule ); result.append( session.doCommand( rule ) ); } } } } getOpenRules( status, vid, gateSetup, addr1, addr2, acl ) { log.debug( "geting open rules..."); return getRules( status, "\\[OPEN\\](.*)\\[/OPEN\\]", vid, gateSetup, addr1, addr2, acl ); } getCloseRules( status, vid, gateSetup, addr1, addr2, acl ) { log.debug( "geting close rules..."); return getRules( status, "\\[CLOSE\\](.*)\\[/CLOSE\\]", vid, gateSetup, addr1, addr2, acl ); } getRemoveRules( UserStatus status, vid, gateSetup, addr1, addr2, acl ) { log.debug( "geting remove rules..."); return getRules( status, "\\[REMOVE\\](.*)\\[/REMOVE\\]", vid, gateSetup, addr1, addr2, acl ); } getRules( status, template, vid, gateSetup, addr1, addr2, acl ) { // пользовательское правило, без типа - то все оставляем как есть CiscoRule ciscoRule = new CiscoRule ( status.rule.getRuleText() ); rule = ciscoRule.getAddresses(); //log.info("rule=" + rule); //типизированное правило if( status.ruleType != null ) { rule = generateRule( rule, status.gateType, status.ruleType, vid, status.contractId, gateSetup, addr1, addr2, acl ); } //log.info("rule=" + rule); pattern = Pattern.compile( template, Pattern.DOTALL ); m = pattern.matcher( rule ); if (m.find()) { rule = m.group( 1 ); } rule.replaceAll( "\r", "" ); parts = rule.split( "\n" ); result = new ArrayList(); for ( String part : parts ) { if ( !Utils.isEmptyString( part )) { result.add( part ); } } return result; } generateRule( addresses, gateType, ruleType, int vid, int cid, gateSetup, addr1, addr2, acl ) { supervlan = gateSetup.getIntValue( "supervlan", 0 ); Map replacements = new HashMap(); if ( addr1 != null && addr2 != null ) { replacements.put( "\\{addr1\\}", addr1 ); replacements.put( "\\{addr2\\}", addr2 ); replacements.put( "\\{SUPERVLAN\\}", String.valueOf( supervlan) ); replacements.put( "\\{ACL\\}", acl ); } ruleText = ManadUtils.getRule( gateType, ruleType ); if ( vid > 0) { replacements.put( "\\{VID\\}", String.valueOf( vid ) ); } return ManadUtils.generateRule( ruleText, addresses, replacements, ruleType ); } private isUserDenied ( addr1, addr2, buffer) { //эскейпируем точки в адресе String address1 = addr1.replace( ".", "\\." ); String address2 = addr2.replace( ".", "\\." ); return isUserDeniedForAddress( address1, buffer ) && isUserDeniedForAddress( address2, buffer ); } private isUserDeniedForAddress ( address, buffer) { //эскейпируем точки в адресе //String address = addr1.replace( ".", "\\." ); //log.debug( "aaddr1=" + address ); String template = ".*deny ip host\\s+" + address + "\\s+any.*$"; //log.debug( "template=" + template ); Pattern pattern = Pattern.compile( template, Pattern.DOTALL ); Matcher m = pattern.matcher( buffer ); boolean found = m.find(); if ( found ) { log.debug( "user2 denied" ); return true; } pattern = Pattern.compile( ".*deny ip any host\\s" + address + "\\s.*$", Pattern.DOTALL ); m = pattern.matcher( buffer ); found = m.find(); log.debug( found ? "user denied" :"user not denied" ); return found; } private vlanExists ( vlan, buffer) { Pattern pattern = Pattern.compile( ".*" + vlan + "\\s+ON\\s+.*", Pattern.DOTALL ); Matcher m = pattern.matcher( buffer ); found = m.find(); log.debug( found ? "vlan found" : "vlan not found" ); return found; } private getVlanResult( session ) { return session.doCommand( "show supervlan"); } private getAclResult( session, acl ) { return session.doCommand( "show access-lists " + acl ); }
конфигурация шлюза Доступа Dlink
user_rule.editor.class=bitel.billing.module.services.ipn.editor.vlan.CiscoSSHSwitchRuleEditor gate_manager.class=bitel.billing.server.ipn.vlan.CiscoSSHSwitchGateWorker use.script=1
Команды шлюза доступа
[DEFAULT] [OPEN] create vlan vlan{VID} tag {VID} config vlan vlan{VID} add tagged {UPLINK_PORT} <LOOP_PORT> config vlan vlan{VID} add untagged {PORT} config ports {PORT} description {CONTRACT_NAME} </LOOP_PORT> save config [/OPEN] [CLOSE] [/CLOSE] [REMOVE] delete vlan vlan{VID} <LOOP_PORT> config ports {PORT} clear_description </LOOP_PORT> save config [/REMOVE] [/DEFAULT]
Скрипт шлюза доступа
import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import bitel.billing.common.IPUtils; import bitel.billing.common.module.ipn.IPNContractStatus; import bitel.billing.server.ipn.UserStatus; import bitel.billing.server.ipn.dlink.UserPortStatus; import bitel.billing.server.util.DefaultServerSetup; import bitel.billing.server.util.Utils; import bitel.billing.server.util.telnet.OperationTimedoutException; import bitel.billing.server.util.telnet.TelnetSession; import bitel.billing.server.ipn.bean.*; import bitel.billing.server.contract.bean.*; import bitel.billing.server.util.telnet.ansi.TelnetAnsiSession; import ru.bitel.bgbilling.modules.ipn.server.bean.GatePortManager; protected void doSync() { String host = gate.getHost(); int port = gate.getPort(); DefaultServerSetup gateSetup = new DefaultServerSetup( gate.getConfig(), "\r\n" ); String pswd = gate.getKeyword(); String login = gateSetup.getStringValue( "login"); StringBuffer result = new StringBuffer(); if( log.isDebugEnabled() ) { log.debug( gate.getId() + " gate: " + host + ":" + port + " login: " + login + " pswd: " + pswd ); } TelnetAnsiSession session = new TelnetAnsiSession( host, port); session.setLoginPromptSequence( "UserName:" ); log.debug( "before connect" ); result.append( session.connect() ); log.debug( "after connect" ); session.setLoginPromptSequence( ":" ); result.append( session.doCommand( login ) ); log.debug( "after command" ); session.setLoginPromptSequence( "#" ); result.append( session.doCommand( pswd ) ); log.debug( "before pswd" ); log.debug( "execute commands" ); doCommands( session, result, gateSetup); session.setLoginPromptSequence( "****" ); result.append( session.doCommand( "logout" ) ); log.info( "!!!!!!!!!!!!!!!!!!!!!!!!!!! Dlink 3028 !!!!!!!!!!!!!!!!!!!!!!!!!!!!" ); log.info( result.toString() ); log.info( "!!!!!!!!!!!!!!!!!!!!!!!!!!! end of Dlink 3028 !!!!!!!!!!!!!!!!!!!!!!!!!!!!" ); log.debug( "ok" ); } private void doCommands( session, result, DefaultServerSetup gateSetup ) throws IOException, OperationTimedoutException { VlanManager manager = new VlanManager(mid, con); ContractManager cm = new ContractManager( con ); StringBuilder resultCheck = new StringBuilder(); session.setLoginPromptSequence( "All" ); resultCheck.append( session.doCommand("show vlan ports 1-28" ) ); session.setLoginPromptSequence( "#" ); resultCheck.append( session.doCommand("all" ) ); Map vlanPortMap = readPortVlanMap( resultCheck.toString() ); log.debug("!!!!!!!!vlan-port check!!!!!!!!!!!!"); for ( entry : vlanPortMap.entrySet() ) { log.debug("port " + entry.getKey() ); for ( s : entry.getValue() ) { log.debug("vlan " + s ); } } log.debug( "!!!!!!!!end vlan-port check!!!!!!!!!!!!" ); for( UserStatus status : statusList ) { String addrs = null; int port = -1 ; List ports = new GatePortManager( con, mid ).getGatePorts( status.contractId, gate.getId() ); //типизированное правило if( status.ruleType != null ) { rule = status.rule.getRuleText(); addrs = getAddresses( rule ); if ( ports.size() > 0 ) { port = ports.get(0); log.debug( "port=" + port ); } } //берем vlan у родительской циски int vid = manager.getVlan( gate.getParentId(), status.contractId ); //провереям только один порт Set vlans = vlanPortMap.get( port ); boolean exists = vlans != null && vlans.contains( vid ); log.debug( "exists=" + exists ); String[] rules = new String[0]; Contract c = cm.getContractByID( status.contractId ); String title = c.getTitle(); if ( status.status == IPNContractStatus.STATUS_OPEN && !exists ) { rules = getOpenRules( status, vid, title, gateSetup, addrs, ports ); } else if ( status.status == IPNContractStatus.STATUS_REMOVED && exists ) { rules = getRemoveRules( status, vid, title, gateSetup, addrs, ports ); } //close and etc..пока плевать. т.к они все равно пустые else { rules = getCloseRules( status, vid, title, gateSetup, addrs, ports ); } for ( String rule : rules ) { log.debug( "command=" + rule ); result.append( session.doCommand( rule ) ); } } } private String[] getOpenRules( UserStatus status, int vid, title, gateSetup, addrs, ports ) { return getRules( status, "\\[OPEN\\](.*)\\[/OPEN\\]", vid, title, gateSetup, addrs, ports ); } private String[] getCloseRules( UserStatus status, int vid, title, gateSetup, addrs, ports ) { return getRules( status, "\\[CLOSE\\](.*)\\[/CLOSE\\]", vid, title, gateSetup, addrs, ports ); } private String[] getRemoveRules( UserStatus status, int vid, title, gateSetup, addrs, ports ) { return getRules( status, "\\[REMOVE\\](.*)\\[/REMOVE\\]", vid, title, gateSetup, addrs, ports); } getRules( status, template, vid, title, gateSetup, addrs, ports ) { // пользовательское правило, без типа - то все оставляем как есть rule = status.rule.getRuleText(); //типизированное правило if( status.ruleType != null ) { rule = generateRule( addrs, ports, vid, status.gateType, status.ruleType, title, gateSetup ); } pattern = Pattern.compile( template, Pattern.DOTALL ); m = pattern.matcher( rule ); if (m.find()) { rule = m.group( 1 ); } rule.replaceAll( "\r", "" ); parts = rule.split( "\n" ); return parts; } generateRule( addreses, ports, vid, gateType, ruleType, title, gateSetup ) { ruleText = ManadUtils.getRule( gateType, ruleType ); replacements = new HashMap(); if ( vid > 0) { replacements.put( "\\{VID\\}", String.valueOf( vid ) ); } uplink = gateSetup.getIntValue( "uplink.port", 0 ); replacements.put( "\\{UPLINK_PORT\\}", String.valueOf( uplink ) ); replacements.put( "\\{CONTRACT_NAME\\}", title ); loops = ManadUtils.getAddresLoops( addreses ); String [] portArray = new String[ports.size()]; for ( int i = 0; i < ports.size(); i++ ) { portArray[i] = String.valueOf( ports.get(i) ); } //адреса p = new LoopPattern(); p.setLoopPatern( "LOOP_PORT" ); p.setReplace( "\\{PORT\\}" ); p.setObjects( portArray ); loops.add( p ); return ManadUtils.generateRule( ruleText, replacements, ruleType, loops ); } String getAddresses( rule ) { Map result = new HashMap(); //порты идут до #, а адреса идут после String [] parts = rule.split( "#" ); addresesStr = ""; if ( parts.length > 1 ) { addresesStr = parts[1]; } /* portsStr = ""; if ( parts.length > 0 ) { portsStr = parts[0]; } portList = new ArrayList(); parts = portsStr.split( ";" ); for (String part : parts ) { parts2 = part.split( ":" ); if ( parts2.length < 2) { continue; } port = parts2 [1]; portList.add( port ); } String [] ports_ = new String [portList.size()]; for (int i = 0; i < ports_.length; i++) { ports_[i] = portList.get( i ); } result.put( "ports", ports_); result.put( "addrs", addresesStr); */ return addresesStr; } readPortVlanMap( String data ) { Map result = new HashMap(); String [] lines = data.split( "\n" ); String templatePort = ".*?Port\\s(\\d+).*?$"; Pattern patternPort = Pattern.compile( templatePort, Pattern.DOTALL ); Matcher m = patternPort.matcher( data ); String templateVlan = ".*?(\\d+)\\s+X.*?$"; Pattern patternVlan = Pattern.compile( templateVlan, Pattern.DOTALL ); int port = 1; for ( String line : lines ) { m = patternPort.matcher( line ); if ( m.find() ) { port = Integer.valueOf( m.group( 1 ) ); continue; } m = patternVlan.matcher( line ); if ( m.find() ) { int vlan = Integer.valueOf( m.group( 1 ) ); Set vlans = result.get( port ); if ( vlans == null ) { vlans = new HashSet(); result.put( port, vlans ); } vlans.add( vlan ); } //2 - X } return result; }