add session/idle timeouts
This commit is contained in:
parent
67af795b70
commit
65270c4bd2
8 changed files with 133 additions and 55 deletions
3
Changes
3
Changes
|
|
@ -1,10 +1,11 @@
|
||||||
* Wed May 24 2006 Brendan O'Dea <bod@optus.net> 2.2.0
|
* Fri Jun 9 2006 Brendan O'Dea <bod@optus.net> 2.2.0
|
||||||
- Only poll clifd if successfully bound.
|
- Only poll clifd if successfully bound.
|
||||||
- Add "Practical VPNs" document from Liran Tal as Docs/vpn .
|
- Add "Practical VPNs" document from Liran Tal as Docs/vpn .
|
||||||
- Add Multilink support from Khaled Al Hamwi.
|
- Add Multilink support from Khaled Al Hamwi.
|
||||||
- Remove non-working setuid option.
|
- Remove non-working setuid option.
|
||||||
- Convert manual.html to Docbook.
|
- Convert manual.html to Docbook.
|
||||||
- Kludge around problem with Netgear DM602 authentication.
|
- Kludge around problem with Netgear DM602 authentication.
|
||||||
|
- Add session/idle timeouts (Graham Maltby).
|
||||||
|
|
||||||
* Tue Apr 18 2006 Brendan O'Dea <bod@optus.net> 2.1.18
|
* Tue Apr 18 2006 Brendan O'Dea <bod@optus.net> 2.1.18
|
||||||
- Don't shutdown on TerminateReq, wait for CDN.
|
- Don't shutdown on TerminateReq, wait for CDN.
|
||||||
|
|
|
||||||
1
THANKS
1
THANKS
|
|
@ -27,3 +27,4 @@ Paul Martin <pm@zetnet.net>
|
||||||
Jonathan Yarden <jyarden@bluegrass.net>
|
Jonathan Yarden <jyarden@bluegrass.net>
|
||||||
Patrick Cole <z@amused.net>
|
Patrick Cole <z@amused.net>
|
||||||
Khaled Al Hamwi <kh.alhamwi@gmail.com>
|
Khaled Al Hamwi <kh.alhamwi@gmail.com>
|
||||||
|
Graham Maltby <gmaltby+l2tpns@iig.com.au>
|
||||||
|
|
|
||||||
7
cli.c
7
cli.c
|
|
@ -2,7 +2,7 @@
|
||||||
// vim: sw=8 ts=8
|
// vim: sw=8 ts=8
|
||||||
|
|
||||||
char const *cvs_name = "$Name: $";
|
char const *cvs_name = "$Name: $";
|
||||||
char const *cvs_id_cli = "$Id: cli.c,v 1.73 2006-05-05 08:10:18 bodea Exp $";
|
char const *cvs_id_cli = "$Id: cli.c,v 1.74 2006-06-11 12:46:18 bodea Exp $";
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
@ -426,6 +426,11 @@ static int cmd_show_session(struct cli_def *cli, char *command, char **argv, int
|
||||||
cli_print(cli, "\tUnique SID:\t%u", session[s].unique_id);
|
cli_print(cli, "\tUnique SID:\t%u", session[s].unique_id);
|
||||||
cli_print(cli, "\tOpened:\t\t%u seconds", session[s].opened ? abs(time_now - session[s].opened) : 0);
|
cli_print(cli, "\tOpened:\t\t%u seconds", session[s].opened ? abs(time_now - session[s].opened) : 0);
|
||||||
cli_print(cli, "\tIdle time:\t%u seconds", session[s].last_packet ? abs(time_now - session[s].last_packet) : 0);
|
cli_print(cli, "\tIdle time:\t%u seconds", session[s].last_packet ? abs(time_now - session[s].last_packet) : 0);
|
||||||
|
if (session[s].session_timeout)
|
||||||
|
cli_print(cli, "\tSess Timeout:\t%u seconds", session[s].session_timeout - (session[s].opened ? abs(time_now - session[s].opened) : 0));
|
||||||
|
if (session[s].idle_timeout)
|
||||||
|
cli_print(cli, "\tIdle Timeout:\t%u seconds", session[s].idle_timeout - (session[s].last_data ? abs(time_now - session[s].last_data) : 0));
|
||||||
|
|
||||||
cli_print(cli, "\tBytes In/Out:\t%u/%u", session[s].cout, session[s].cin);
|
cli_print(cli, "\tBytes In/Out:\t%u/%u", session[s].cout, session[s].cin);
|
||||||
if (session[s].timeout)
|
if (session[s].timeout)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// L2TPNS Clustering Stuff
|
// L2TPNS Clustering Stuff
|
||||||
|
|
||||||
char const *cvs_id_cluster = "$Id: cluster.c,v 1.51 2006-04-27 09:53:49 bodea Exp $";
|
char const *cvs_id_cluster = "$Id: cluster.c,v 1.52 2006-06-11 12:46:18 bodea Exp $";
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
@ -654,7 +654,7 @@ void cluster_check_master(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset idle timeouts..
|
// Reset idle timeouts..
|
||||||
session[i].last_packet = time_now;
|
session[i].last_packet = session[i].last_data = time_now;
|
||||||
|
|
||||||
// Reset die relative to our uptime rather than the old master's
|
// Reset die relative to our uptime rather than the old master's
|
||||||
if (session[i].die) session[i].die = TIME;
|
if (session[i].die) session[i].die = TIME;
|
||||||
|
|
@ -1214,7 +1214,9 @@ static int cluster_handle_bytes(uint8_t *data, int size)
|
||||||
session[b->sid].cout_delta += b->cout;
|
session[b->sid].cout_delta += b->cout;
|
||||||
|
|
||||||
if (b->cin)
|
if (b->cin)
|
||||||
session[b->sid].last_packet = time_now; // Reset idle timer!
|
session[b->sid].last_packet = session[b->sid].last_data = time_now;
|
||||||
|
else if (b->cout)
|
||||||
|
session[b->sid].last_data = time_now;
|
||||||
|
|
||||||
size -= sizeof(*b);
|
size -= sizeof(*b);
|
||||||
++b;
|
++b;
|
||||||
|
|
|
||||||
40
l2tpns.c
40
l2tpns.c
|
|
@ -4,7 +4,7 @@
|
||||||
// Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced
|
// Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced
|
||||||
// vim: sw=8 ts=8
|
// vim: sw=8 ts=8
|
||||||
|
|
||||||
char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.166 2006-05-16 06:46:37 bodea Exp $";
|
char const *cvs_id_l2tpns = "$Id: l2tpns.c,v 1.167 2006-06-11 12:46:18 bodea Exp $";
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
@ -1075,6 +1075,7 @@ void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t e
|
||||||
p += 2;
|
p += 2;
|
||||||
l -= 2;
|
l -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proto == PPPIP)
|
if (proto == PPPIP)
|
||||||
{
|
{
|
||||||
if (session[s].die)
|
if (session[s].die)
|
||||||
|
|
@ -1082,7 +1083,8 @@ void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t e
|
||||||
LOG(4, s, t, "MPPP: Session %u is closing. Don't process PPP packets\n", s);
|
LOG(4, s, t, "MPPP: Session %u is closing. Don't process PPP packets\n", s);
|
||||||
return; // closing session, PPP not processed
|
return; // closing session, PPP not processed
|
||||||
}
|
}
|
||||||
session[s].last_packet = time_now;
|
|
||||||
|
session[s].last_packet = session[s].last_data = time_now;
|
||||||
processipin(s, t, p, l);
|
processipin(s, t, p, l);
|
||||||
}
|
}
|
||||||
else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0])
|
else if (proto == PPPIPV6 && config->ipv6_prefix.s6_addr[0])
|
||||||
|
|
@ -1093,7 +1095,7 @@ void processmpframe(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l, uint8_t e
|
||||||
return; // closing session, PPP not processed
|
return; // closing session, PPP not processed
|
||||||
}
|
}
|
||||||
|
|
||||||
session[s].last_packet = time_now;
|
session[s].last_packet = session[s].last_data = time_now;
|
||||||
processipv6in(s, t, p, l);
|
processipv6in(s, t, p, l);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -1166,6 +1168,7 @@ static void processipout(uint8_t *buf, int len)
|
||||||
}
|
}
|
||||||
t = session[s].tunnel;
|
t = session[s].tunnel;
|
||||||
sp = &session[s];
|
sp = &session[s];
|
||||||
|
sp->last_data = time_now;
|
||||||
|
|
||||||
// DoS prevention: enforce a maximum number of packets per 0.1s for a session
|
// DoS prevention: enforce a maximum number of packets per 0.1s for a session
|
||||||
if (config->max_packets > 0)
|
if (config->max_packets > 0)
|
||||||
|
|
@ -1363,6 +1366,7 @@ static void processipv6out(uint8_t * buf, int len)
|
||||||
}
|
}
|
||||||
t = session[s].tunnel;
|
t = session[s].tunnel;
|
||||||
sp = &session[s];
|
sp = &session[s];
|
||||||
|
sp->last_data = time_now;
|
||||||
|
|
||||||
// FIXME: add DoS prevention/filters?
|
// FIXME: add DoS prevention/filters?
|
||||||
|
|
||||||
|
|
@ -2642,7 +2646,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
|
||||||
session[s].opened = time_now;
|
session[s].opened = time_now;
|
||||||
session[s].tunnel = t;
|
session[s].tunnel = t;
|
||||||
session[s].far = asession;
|
session[s].far = asession;
|
||||||
session[s].last_packet = time_now;
|
session[s].last_packet = session[s].last_data = time_now;
|
||||||
LOG(3, s, t, "New session (%u/%u)\n", tunnel[t].far, session[s].far);
|
LOG(3, s, t, "New session (%u/%u)\n", tunnel[t].far, session[s].far);
|
||||||
control16(c, 14, s, 1); // assigned session
|
control16(c, 14, s, 1); // assigned session
|
||||||
controladd(c, asession, t); // send the reply
|
controladd(c, asession, t); // send the reply
|
||||||
|
|
@ -2803,7 +2807,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
|
||||||
return; // closing session, PPP not processed
|
return; // closing session, PPP not processed
|
||||||
}
|
}
|
||||||
|
|
||||||
session[s].last_packet = time_now;
|
session[s].last_packet = session[s].last_data = time_now;
|
||||||
if (session[s].walled_garden && !config->cluster_iam_master)
|
if (session[s].walled_garden && !config->cluster_iam_master)
|
||||||
{
|
{
|
||||||
master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
|
master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
|
||||||
|
|
@ -2820,7 +2824,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
|
||||||
return; // closing session, PPP not processed
|
return; // closing session, PPP not processed
|
||||||
}
|
}
|
||||||
|
|
||||||
session[s].last_packet = time_now;
|
session[s].last_packet = session[s].last_data = time_now;
|
||||||
if (session[s].walled_garden && !config->cluster_iam_master)
|
if (session[s].walled_garden && !config->cluster_iam_master)
|
||||||
{
|
{
|
||||||
master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
|
master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
|
||||||
|
|
@ -2837,7 +2841,7 @@ void processudp(uint8_t *buf, int len, struct sockaddr_in *addr)
|
||||||
return; // closing session, PPP not processed
|
return; // closing session, PPP not processed
|
||||||
}
|
}
|
||||||
|
|
||||||
session[s].last_packet = time_now;
|
session[s].last_packet = session[s].last_data = time_now;
|
||||||
if (session[s].walled_garden && !config->cluster_iam_master)
|
if (session[s].walled_garden && !config->cluster_iam_master)
|
||||||
{
|
{
|
||||||
master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
|
master_forward_packet(buf, len, addr->sin_addr.s_addr, addr->sin_port);
|
||||||
|
|
@ -3195,6 +3199,24 @@ static void regular_cleanups(double period)
|
||||||
s_actions++;
|
s_actions++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drop sessions who have reached session_timeout seconds
|
||||||
|
if (session[s].session_timeout && (time_now - session[s].opened >= session[s].session_timeout))
|
||||||
|
{
|
||||||
|
sessionshutdown(s, "Session Timeout Reached", CDN_ADMIN_DISC, TERM_SESSION_TIMEOUT);
|
||||||
|
STAT(session_timeout);
|
||||||
|
s_actions++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop sessions who have reached idle_timeout seconds
|
||||||
|
if (session[s].last_data && session[s].idle_timeout && (time_now - session[s].last_data >= session[s].idle_timeout))
|
||||||
|
{
|
||||||
|
sessionshutdown(s, "Idle Timeout Reached", CDN_ADMIN_DISC, TERM_IDLE_TIMEOUT);
|
||||||
|
STAT(session_timeout);
|
||||||
|
s_actions++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for actions requested from the CLI
|
// Check for actions requested from the CLI
|
||||||
if ((a = cli_session_actions[s].action))
|
if ((a = cli_session_actions[s].action))
|
||||||
{
|
{
|
||||||
|
|
@ -4876,7 +4898,7 @@ int sessionsetup(sessionidt s, tunnelidt t)
|
||||||
if (session[s].throttle_in || session[s].throttle_out)
|
if (session[s].throttle_in || session[s].throttle_out)
|
||||||
throttle_session(s, session[s].throttle_in, session[s].throttle_out);
|
throttle_session(s, session[s].throttle_in, session[s].throttle_out);
|
||||||
|
|
||||||
session[s].last_packet = time_now;
|
session[s].last_packet = session[s].last_data = time_now;
|
||||||
|
|
||||||
LOG(2, s, t, "Login by %s at %s from %s (%s)\n", session[s].user,
|
LOG(2, s, t, "Login by %s at %s from %s (%s)\n", session[s].user,
|
||||||
fmtaddr(htonl(session[s].ip), 0),
|
fmtaddr(htonl(session[s].ip), 0),
|
||||||
|
|
@ -5439,7 +5461,7 @@ int cmd_show_hist_idle(struct cli_def *cli, char *command, char **argv, int argc
|
||||||
if (!session[s].opened)
|
if (!session[s].opened)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
idle = time_now - session[s].last_packet;
|
idle = time_now - session[s].last_data;
|
||||||
idle /= 5 ; // In multiples of 5 seconds.
|
idle /= 5 ; // In multiples of 5 seconds.
|
||||||
if (idle < 0)
|
if (idle < 0)
|
||||||
idle = 0;
|
idle = 0;
|
||||||
|
|
|
||||||
5
l2tpns.h
5
l2tpns.h
|
|
@ -1,5 +1,5 @@
|
||||||
// L2TPNS Global Stuff
|
// L2TPNS Global Stuff
|
||||||
// $Id: l2tpns.h,v 1.116 2006-04-27 14:37:28 bodea Exp $
|
// $Id: l2tpns.h,v 1.117 2006-06-11 12:46:18 bodea Exp $
|
||||||
|
|
||||||
#ifndef __L2TPNS_H__
|
#ifndef __L2TPNS_H__
|
||||||
#define __L2TPNS_H__
|
#define __L2TPNS_H__
|
||||||
|
|
@ -284,7 +284,10 @@ typedef struct
|
||||||
uint16_t mru; // maximum receive unit
|
uint16_t mru; // maximum receive unit
|
||||||
clockt opened; // when started
|
clockt opened; // when started
|
||||||
clockt die; // being closed, when to finally free
|
clockt die; // being closed, when to finally free
|
||||||
|
uint32_t session_timeout; // Maximum session time in seconds
|
||||||
|
uint32_t idle_timeout; // Maximum idle time in seconds
|
||||||
time_t last_packet; // Last packet from the user (used for idle timeouts)
|
time_t last_packet; // Last packet from the user (used for idle timeouts)
|
||||||
|
time_t last_data; // Last data packet to/from the user (used for idle timeouts)
|
||||||
in_addr_t dns1, dns2; // DNS servers
|
in_addr_t dns1, dns2; // DNS servers
|
||||||
routet route[MAXROUTE]; // static routes
|
routet route[MAXROUTE]; // static routes
|
||||||
uint16_t tbf_in; // filter bucket for throttling in from the user.
|
uint16_t tbf_in; // filter bucket for throttling in from the user.
|
||||||
|
|
|
||||||
|
|
@ -43,5 +43,5 @@ rm -rf %{buildroot}
|
||||||
%attr(644,root,root) /usr/share/man/man[58]/*
|
%attr(644,root,root) /usr/share/man/man[58]/*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Wed May 24 2006 Brendan O'Dea <bod@optus.net> 2.2.0-1
|
* Fri Jun 9 2006 Brendan O'Dea <bod@optus.net> 2.2.0-1
|
||||||
- 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes
|
- 2.2.0 release, see /usr/share/doc/l2tpns-2.2.0/Changes
|
||||||
|
|
|
||||||
52
radius.c
52
radius.c
|
|
@ -1,6 +1,6 @@
|
||||||
// L2TPNS Radius Stuff
|
// L2TPNS Radius Stuff
|
||||||
|
|
||||||
char const *cvs_id_radius = "$Id: radius.c,v 1.50 2006-04-27 09:53:50 bodea Exp $";
|
char const *cvs_id_radius = "$Id: radius.c,v 1.51 2006-06-11 12:46:18 bodea Exp $";
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
@ -330,6 +330,7 @@ void radiussend(uint16_t r, uint8_t state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s)
|
if (s)
|
||||||
{
|
{
|
||||||
*p = 5; // NAS-Port
|
*p = 5; // NAS-Port
|
||||||
|
|
@ -346,15 +347,16 @@ void radiussend(uint16_t r, uint8_t state)
|
||||||
p[1] = 6;
|
p[1] = 6;
|
||||||
*(uint32_t *) (p + 2) = htonl(1); // PPP
|
*(uint32_t *) (p + 2) = htonl(1); // PPP
|
||||||
p += p[1];
|
p += p[1];
|
||||||
}
|
|
||||||
if (s && session[s].ip)
|
if (session[s].ip)
|
||||||
{
|
{
|
||||||
*p = 8; // Framed-IP-Address
|
*p = 8; // Framed-IP-Address
|
||||||
p[1] = 6;
|
p[1] = 6;
|
||||||
*(uint32_t *) (p + 2) = htonl(session[s].ip);
|
*(uint32_t *) (p + 2) = htonl(session[s].ip);
|
||||||
p += p[1];
|
p += p[1];
|
||||||
}
|
}
|
||||||
if (s && session[s].route[0].ip)
|
|
||||||
|
if (session[s].route[0].ip)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
for (r = 0; s && r < MAXROUTE && session[s].route[r].ip; r++)
|
for (r = 0; s && r < MAXROUTE && session[s].route[r].ip; r++)
|
||||||
|
|
@ -378,6 +380,23 @@ void radiussend(uint16_t r, uint8_t state)
|
||||||
p += p[1];
|
p += p[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (session[s].session_timeout)
|
||||||
|
{
|
||||||
|
*p = 27; // Session-Timeout
|
||||||
|
p[1] = 6;
|
||||||
|
*(uint32_t *) (p + 2) = htonl(session[s].session_timeout);
|
||||||
|
p += p[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session[s].idle_timeout)
|
||||||
|
{
|
||||||
|
*p = 28; // Idle-Timeout
|
||||||
|
p[1] = 6;
|
||||||
|
*(uint32_t *) (p + 2) = htonl(session[s].idle_timeout);
|
||||||
|
p += p[1];
|
||||||
|
}
|
||||||
|
|
||||||
if (*session[s].called)
|
if (*session[s].called)
|
||||||
{
|
{
|
||||||
*p = 30; // called
|
*p = 30; // called
|
||||||
|
|
@ -385,6 +404,7 @@ void radiussend(uint16_t r, uint8_t state)
|
||||||
strcpy((char *) p + 2, session[s].called);
|
strcpy((char *) p + 2, session[s].called);
|
||||||
p += p[1];
|
p += p[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*session[s].calling)
|
if (*session[s].calling)
|
||||||
{
|
{
|
||||||
*p = 31; // calling
|
*p = 31; // calling
|
||||||
|
|
@ -392,6 +412,8 @@ void radiussend(uint16_t r, uint8_t state)
|
||||||
strcpy((char *) p + 2, session[s].calling);
|
strcpy((char *) p + 2, session[s].calling);
|
||||||
p += p[1];
|
p += p[1];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NAS-IP-Address
|
// NAS-IP-Address
|
||||||
*p = 4;
|
*p = 4;
|
||||||
p[1] = 6;
|
p[1] = 6;
|
||||||
|
|
@ -699,6 +721,28 @@ void processrad(uint8_t *buf, int len, char socket_index)
|
||||||
ip_filters[f].used++;
|
ip_filters[f].used++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (*p == 27)
|
||||||
|
{
|
||||||
|
// Session-Timeout
|
||||||
|
uint32_t to = ntohl(*(uint32_t *)(p + 2));
|
||||||
|
|
||||||
|
LOG(3, s, session[s].tunnel, " Radius reply contains Session-Timeout = %u\n", to);
|
||||||
|
if (to > 0)
|
||||||
|
{
|
||||||
|
session[s].session_timeout = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*p == 28)
|
||||||
|
{
|
||||||
|
// Idle-Timeout
|
||||||
|
uint32_t to = ntohl(*(uint32_t *)(p + 2));
|
||||||
|
|
||||||
|
LOG(3, s, session[s].tunnel, " Radius reply contains Idle-Timeout = %u\n", to);
|
||||||
|
if (to > 0)
|
||||||
|
{
|
||||||
|
session[s].idle_timeout = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (*p == 26 && p[1] >= 7)
|
else if (*p == 26 && p[1] >= 7)
|
||||||
{
|
{
|
||||||
// Vendor-Specific Attribute
|
// Vendor-Specific Attribute
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue