Дерево страниц

Сравнение версий

Ключ

  • Эта строка добавлена.
  • Эта строка удалена.
  • Изменено форматирование.

...

Для корректной работы нужно правильно извлекать значения agentRemoteId, circuitId (port/VLAN) из DHCP-пакета. А в случае использования IPoE c Cisco ISG или SmartEdge еще и из RADIUS-пакетов (в этом случае субопции опции значения субопций option 82 находятся внутри RADIUS-пакетов).

Для извлечения значения требуется указать код субопции, а также один или два regex, hex и string. hex указывает, что нужно выбрать у значения 16-ричном формате (один байт - это два символа), string - указывает, что нужно выбрать в значении, преобразованном в строку. Предположим, у нас в DHCP-пакете присутствует такая option 82:

Блок кода
languageruby
# Нужно указать код субопции опции 82 для извлечения значений
# agentRemoteId обычно находится в субопции 2
dhcp.option82.agentRemoteId.code=2
# interfaceId обычно находится в субопции 1 (circuitId)
dhcp.option82.interfaceId.code=1
# vlanId обычно находится в субопции 1 (circuitId)
dhcp.option82.vlanId.code=1
Agent information{82}=
sub{1}={076D00020201}
sub{2}={D067B3932607}

agentRemoteId является вся hex-строка D067B3932607 в субопции 2 - это MAC-адрес, укажем как его извлечь:

Блок кода
languageruby
dhcp.option82.agentRemoteId.code=2

dhcp.option82.agentRemoteId.hex=.*

Чтобы получить номер порта, который находится в субопции 1 в третьем байте ({076D00020201}), указываем:

Блок кода
languageruby
dhcp.option82.interfaceId.code=1
dhcp.option82.interfaceId.hex=^\w{6}(\w{2})

Таким образом с помощью regex мы указываем, что нам нужны два символа (02) в этой hex-строке.

Чтобы получить VLAN, который находится в субопции 1 в первых двух байтах ({076D00020201})

Блок кода
languageruby
dhcp.option82.vlanId.code=1
dhcp.option82.vlanId.hex=^(\w{2})

Подобным образом можно извлечь SVLAN:

Блок кода
languageruby
dhcp.option82.agentSvlanId.code=1
dhcp.option82.agentSvlanId.hex=^(\w{2})

Или логин для поиска по логину (для схемы PON, когда в agentRemoteId приходит MAC-адрес ONU):

Блок кода
languageruby
dhcp.option82.login.code=2
dhcp.option82.login.hex=.*

Однако приходящие значения могут представлять собой строку:

Блок кода
languageruby
Agent information{82}=
sub{1}={7072617664792D3331612D706F7274332D3330352D766C616E}
sub{2}={33362D6C65736F7A61766F642065746820312F313235}

Такие значения обычно длиннее, чем бинарный вариант. В данном примере в опции 1 записана строка "pravdy-31a-port3-305-vlan", в опции 2 - "36-lesozavod eth 1/125". Для извлечения значения из такой опции можно использовать regex, прописанный в параметре string:


Блок кода
languageruby
dhcp.option82.interfaceId.code=1
dhcp.option82.interfaceId.string=port(\d+)
Блок кода
languageruby
dhcp.option82.vlanId.code=1
dhcp.option82.vlanId.string=(\d+)-vlan

Если hex при этом не указан, то это равнозначно, как если бы он был указан равным ".*":


Блок кода
languageruby
dhcp.option82.interfaceId.code=1
dhcp.option82.interfaceId.hex=.*
dhcp.option82.interfaceId.string=port(\d+)

Если в 16-ричном виде присутствует префикс, который нужно исключить перед преобразованием в строку, то можно использовать оба regex - сначала с помощью hex выберем 16-ричное значение, затем с помощью string преобразуем его в строку и выберем подстроку. Например, у нас в значении есть префикс-длина:


Блок кода
languageruby
Agent information{82}=
sub{1}={00197072617664792D3331612D706F7274332D3330352D766C616E}
sub{2}={001633362D6C65736F7A61766F642065746820312F313235}

Тогда исключить первые два байта и получить порт можем так:


Блок кода
languageruby
dhcp.option82.interfaceId.code=1
dhcp.option82.interfaceId.hex=^.{4}(.+)$
dhcp.option82.interfaceId.string=port(\d+)

Regex-ов можно указать несколько - это может быть удобно при наличии устройств разных марок - они могут посылать option 82 в разном формате:


Блок кода
languageruby
dhcp.option82.agentRemoteId.code=2
dhcp.option82.agentRemoteId.1.hex=^0006(.{6})$
dhcp.option82.agentRemoteId.2.hex=.*

Для таких случаев рекомендуется, чтобы в дереве устройств биллинга было устройство с таким идентификатором (agentRemoteId), у него был свой тип устройства, а в конфигурации типа устройства была задана конфигурация для извлечения порт/VLAN и т.п., но если разница можно выделить через regex, то извлечение полей circuitId можно также произвести набором regex:

Блок кода
languageruby
dhcp.option82.interfaceId.code=1
dhcp.option82.interfaceId.1.hex=^0006\w{2}(\w{2})
dhcp.option82.interfaceId.2.hex=.*
dhcp.option82.interfaceId.2.string=port(\d+)

Извлечение значений, идентифицирующих абонента из DHCP-пакета, c указанием позиции и длины

Это старый вариант извлечения значений, но при больших нагрузках (от 300 тыс. сессий онлайн) он может быть предпочтительнее, т.к. будет тратить меньше ресурсов процессора.

Блок кода
languageruby
# Если в субопции отсутствует заголовок с длиной субопции, то укажите 0. Иначе укажите длину заголовка.
# Данный параметр используется в том числе, для того, чтобы извлеченные значения circuitId из DHCP-пакета и из RADIUS-пакета были идентичны.
# Соответственно, значение position нужно указывать относительно removeHeader.
dhcp.option82.removeHeader=0
Блок кода
languageruby
# НужноagentRemoteId указатьобычно параметрынаходится извлеченияв agentRemoteIdсубопции из2
субопции (dhcp.option82.agentRemoteId.code)=2
# позиция значения внутри субопции
dhcp.option82.agentRemoteId.position=2
# если длина значения может изменятся и нужно брать значение до конца субопции, то укажите -1
dhcp.option82.agentRemoteId.length=6
# 0, если remoteId в бинарном виде, например, MAC-адрес; 1, если там закодирована строка
dhcp.option82.agentRemoteId.type=0
Блок кода
languageruby
# НужноinterfaceId указатьобычно параметрынаходится извлеченияв interfaceIdсубопции из1 субопции(circuitId)
(dhcp.option82.interfaceId.code)
=1
# позиция значения внутри субопции
dhcp.option82.interfaceId.position=5
# длина значения
dhcp.option82.interfaceId.length=1
Блок кода
languageruby
# НужноvlanId указатьобычно параметрынаходится извлеченияв interfaceIdсубопции из1 субопции(circuitId)
(dhcp.option82.vlanId.code)
=1
# позиция значения внутри субопции
dhcp.option82.vlanId.position=2
# длина значения
dhcp.option82.vlanId.length=2

...

В этом случае нельзя однозначно указать в конфигурации как извлечь agentRemoteId, в отличие от варианта выше.

Поэтому нужно воспользоваться набором regex, описанных выше:

Блок кода
languageruby
dhcp.option82.agentRemoteId.code=2
dhcp.option82.agentRemoteId.1.hex=^0006(.{6})$
dhcp.option82.agentRemoteId.2.hex=.{8}(.{6})

Или же воспользоваться предобработкой пакетов. Укажите Для этого укажите тип поиска DHCP-устройства = 0 (в этом режиме сначала находится устройство по giaddr, у него вызывается preprocess, затем находится агентское ус-во, у него тоже вызывается preprocess).

...

Расширьте обработчик процессора протокола типа устройства, с с которого приходит запрос на InetAccess (т.е. чей giaddr указан в пакете). По giaddr InetAccess однозначно найдет устройство. Затем вызовет у него предобработку, в которой нужно определить как распарсить и в ручную проставить agentRemoteId.


 

Блок кода
languagejava
import java.util.Arrays;
 
import ru.bitel.bgbilling.kernel.network.dhcp.DhcpPacket;
import ru.bitel.bgbilling.kernel.network.dhcp.DhcpProtocolHandler;
import ru.bitel.bgbilling.modules.inet.access.sa.ProtocolHandlerAdapter;
import ru.bitel.bgbilling.modules.inet.dhcp.InetDhcpProcessor;
 
public class Dhcp82ProtocolHandler
	extends ProtocolHandlerAdapter
	implements DhcpProtocolHandler
{
	@Override
	public void preprocessDhcpRequest( DhcpPacket request, DhcpPacket response )
		throws Exception
	{
		byte[] agentRemoteId = request.getSubOption( (byte)2 ).value;
		// DLink
		if( agentRemoteId.length == 8 )
		{
			request.setOption( InetDhcpProcessor.AGENT_REMOTE_ID, Arrays.copyOfRange( agentRemoteId, 2, 6 ) );
		}
		else
		{
			request.setOption( InetDhcpProcessor.AGENT_REMOTE_ID, Arrays.copyOfRange( agentRemoteId, 5, 6 ) );
		}
	}
}

 


Конфигурация извлечения порта/VLAN из curcuitId должна быть указана в агентских типах устройствах. Таким образом InetAccess найдет relay agent по giaddr, предобработка извлечет и проставит agentRemoteId, по agentRemoteId InetAccess найдет дочернее агентское устройство и уже по его конфигурации извлечет значения порта/VLAN.

...

В этом случае нельзя однозначно указать в конфигурации как извлечь agentRemoteId и curcuitId из RADIUS-пакета. Поэтому это нужно сделать в предобработке (извлечение из RADIUS-пакета и сейчас происходит в предобработке, но согласно конфигурации - это можно увидеть в динамических классах ISGProtocolHandler и SmartEdgeClipsProtocolHandler). Расширьте класс предобработки Cisco ISG/SmartEdge:


 

Блок кода
languagejava
import java.util.Arrays;
 
import ru.bitel.bgbilling.kernel.network.radius.RadiusAttribute;
import ru.bitel.bgbilling.kernel.network.radius.RadiusPacket;
import ru.bitel.bgbilling.modules.inet.radius.InetRadiusProcessor;
 
public class XSmartEdgeClipsProtocolHandler
	extends SmartEdgeClipsProtocolHandler
{
	@Override
	protected void setAgentOptions( RadiusPacket request )
	{
		RadiusAttribute<byte[]> agentRemoteIdAttribute = request.getAttribute( 2352, 96 );
		RadiusAttribute<byte[]> circuitRemoteIdAttribute = request.getAttribute( 2352, 97 );
 
		byte[] agentRemoteId = agentRemoteIdAttribute.getValue();
		byte[] circuitRemoteId = circuitRemoteIdAttribute.getValue();
 
		// DLink
		if( agentRemoteId.length == 8 )
		{
			request.setOption( InetRadiusProcessor.AGENT_REMOTE_ID, Arrays.copyOfRange( agentRemoteId, 2, 6 ) );
			request.setOption( InetRadiusProcessor.AGENT_CIRCUIT_ID, Arrays.copyOfRange( circuitRemoteId, 2, circuitRemoteId.length - 2 ) );
		}
		else
		{
			request.setOption( InetRadiusProcessor.AGENT_REMOTE_ID, Arrays.copyOfRange( agentRemoteId, 5, 6 ) );
			request.setOption( InetRadiusProcessor.AGENT_CIRCUIT_ID, Arrays.copyOfRange( circuitRemoteId, 2, circuitRemoteId.length - 2 ) );
		}
	}
}

Если в DHCP-пакете указан giaddr Cisco/SmartEdge, то в этот класс нужно добавить предобработку DHCP

...