Annotation of embedaddon/libpdel/ppp/ppp_l2tp_ctrl.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (c) 2001-2002 Packet Design, LLC.
! 4: * All rights reserved.
! 5: *
! 6: * Subject to the following obligations and disclaimer of warranty,
! 7: * use and redistribution of this software, in source or object code
! 8: * forms, with or without modifications are expressly permitted by
! 9: * Packet Design; provided, however, that:
! 10: *
! 11: * (i) Any and all reproductions of the source or object code
! 12: * must include the copyright notice above and the following
! 13: * disclaimer of warranties; and
! 14: * (ii) No rights are granted, in any manner or form, to use
! 15: * Packet Design trademarks, including the mark "PACKET DESIGN"
! 16: * on advertising, endorsements, or otherwise except as such
! 17: * appears in the above copyright notice or in the software.
! 18: *
! 19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
! 20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
! 21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
! 22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
! 23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
! 24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
! 25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
! 26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
! 27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
! 28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
! 29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
! 30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
! 31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
! 32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
! 33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
! 35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
! 36: * THE POSSIBILITY OF SUCH DAMAGE.
! 37: *
! 38: * Author: Archie Cobbs <archie@freebsd.org>
! 39: */
! 40:
! 41: #include "ppp/ppp_defs.h"
! 42: #include "ppp/ppp_log.h"
! 43: #include "ppp/ppp_l2tp_avp.h"
! 44: #include "ppp/ppp_l2tp_ctrl.h"
! 45: #include <netgraph/ng_message.h>
! 46: #include <netgraph/ng_socket.h>
! 47: #include <netgraph/ng_l2tp.h>
! 48: #include <netgraph/ng_tee.h>
! 49:
! 50: #ifndef __FreeBSD__
! 51: #define __printflike(x,y)
! 52: #endif
! 53:
! 54: /*
! 55: * Bugs:
! 56: *
! 57: * - We don't handle sending or receiving challenge/response
! 58: */
! 59:
! 60: #define CTRL_MEM_TYPE "ppp_l2tp_ctrl"
! 61: #define SESS_MEM_TYPE "ppp_l2tp_sess"
! 62:
! 63: /* Retransmit timeout parameters */
! 64: #define L2TP_REXMIT_MAX 8
! 65: #define L2TP_REXMIT_MAX_TO 20
! 66:
! 67: #define L2TP_CHALLENGE_LEN 16
! 68:
! 69: #define L2TP_CONNECT_SPEED 10000000 /* XXX hardcoded */
! 70: #define L2TP_FRAMING_TYPE L2TP_FRAMING_SYNC /* XXX hardcoded */
! 71:
! 72: /* Idle timeout for sending 'HELLO' message */
! 73: #define L2TP_IDLE_TIMEOUT 60
! 74:
! 75: /* Reply timeout for messages */
! 76: #define L2TP_REPLY_TIMEOUT 60
! 77:
! 78: /* Linger time for dying tunnels and sessions */
! 79: #define L2TP_CTRL_DEATH_TIMEOUT 31
! 80: #define L2TP_SESS_DEATH_TIMEOUT 31
! 81:
! 82: /* Control message types */
! 83: enum l2tp_msg_type {
! 84: SCCRQ =1,
! 85: SCCRP =2,
! 86: SCCCN =3,
! 87: StopCCN =4,
! 88: HELLO =6,
! 89: OCRQ =7,
! 90: OCRP =8,
! 91: OCCN =9,
! 92: ICRQ =10,
! 93: ICRP =11,
! 94: ICCN =12,
! 95: CDN =14,
! 96: WEN =15,
! 97: SLI =16,
! 98: };
! 99:
! 100: /* Control connection states */
! 101: enum l2tp_ctrl_state {
! 102: CS_IDLE = 0,
! 103: CS_WAIT_CTL_REPLY,
! 104: CS_WAIT_CTL_CONNECT,
! 105: CS_ESTABLISHED,
! 106: CS_DYING
! 107: };
! 108:
! 109: /* Session states */
! 110: enum l2tp_sess_state {
! 111: SS_WAIT_REPLY = 1,
! 112: SS_WAIT_CONNECT,
! 113: SS_WAIT_ANSWER,
! 114: SS_ESTABLISHED,
! 115: SS_DYING
! 116: };
! 117:
! 118: /* Session origination */
! 119: enum l2tp_sess_orig {
! 120: ORIG_LOCAL =0,
! 121: ORIG_REMOTE =1
! 122: };
! 123:
! 124: /* Session side */
! 125: enum l2tp_sess_side {
! 126: SIDE_LNS =0,
! 127: SIDE_LAC =1
! 128: };
! 129:
! 130: /*
! 131: * Control message handler function types.
! 132: *
! 133: * Messages are either session-specific, or apply to the entire tunnel.
! 134: */
! 135: typedef int l2tp_ctrl_msg_handler_t(struct ppp_l2tp_ctrl *ctrl,
! 136: const struct ppp_l2tp_avp_list *avps,
! 137: struct ppp_l2tp_avp_ptrs *ptrs);
! 138: typedef int l2tp_sess_msg_handler_t(struct ppp_l2tp_sess *sess,
! 139: const struct ppp_l2tp_avp_list *avps,
! 140: struct ppp_l2tp_avp_ptrs *ptrs);
! 141:
! 142: /* Descriptor for one control message */
! 143: struct l2tp_msg_info {
! 144: const char *name;
! 145: enum l2tp_msg_type type;
! 146: l2tp_ctrl_msg_handler_t *ctrl_handler;
! 147: l2tp_sess_msg_handler_t *sess_handler;
! 148: int valid_states[12];
! 149: int valid_orig[3];
! 150: int valid_side[3];
! 151: int req_avps[AVP_MAX + 1];
! 152: };
! 153:
! 154: /* Control connection */
! 155: struct ppp_l2tp_ctrl {
! 156: enum l2tp_ctrl_state state; /* control state */
! 157: const struct ppp_l2tp_ctrl_cb *cb; /* link callbacks */
! 158: struct pevent_ctx *ctx; /* event context */
! 159: pthread_mutex_t *mutex; /* mutex */
! 160: u_int32_t serial; /* next call serial */
! 161: ng_ID_t node_id; /* l2tp node id */
! 162: u_int32_t peer_id; /* peer unique id */
! 163: char path[32]; /* l2tp node path */
! 164: int csock; /* netgraph ctrl sock */
! 165: int dsock; /* netgraph data sock */
! 166: u_char *secret; /* shared secret */
! 167: u_int seclen; /* share secret len */
! 168: struct ppp_log *log; /* log */
! 169: struct ng_l2tp_config config; /* netgraph node cfg. */
! 170: struct ghash *sessions; /* sessions */
! 171: struct ppp_l2tp_avp_list *avps; /* avps for SCCR[QP] */
! 172: struct pevent *idle_timer; /* ctrl idle timer */
! 173: struct pevent *reply_timer; /* reply timer */
! 174: struct pevent *close_timer; /* close timer */
! 175: struct pevent *death_timer; /* death timer */
! 176: struct pevent *ctrl_event; /* ctrl socket event */
! 177: struct pevent *data_event; /* data socket event */
! 178: void *link_cookie; /* opaque link cookie */
! 179: u_int16_t result; /* close result code */
! 180: u_int16_t error; /* close error code */
! 181: u_int32_t peer_bearer; /* peer bearer types */
! 182: u_int32_t peer_framing; /* peer framing types */
! 183: u_int active_sessions; /* # non-dying sessns */
! 184: char *errmsg; /* close error msg */
! 185: u_char link_notified; /* link notified down */
! 186: u_char peer_notified; /* peer notified down */
! 187: };
! 188:
! 189: /* Session */
! 190: struct ppp_l2tp_sess {
! 191: struct ppp_l2tp_ctrl *ctrl; /* associated ctrl */
! 192: enum l2tp_sess_state state; /* session state */
! 193: enum l2tp_sess_orig orig; /* who originated it? */
! 194: enum l2tp_sess_side side; /* are we lac or lns? */
! 195: u_int32_t serial; /* call serial number */
! 196: struct ng_l2tp_sess_config config; /* netgraph hook cfg. */
! 197: struct ppp_l2tp_avp_list *my_avps; /* avps in [IO]CR[QP] */
! 198: struct ppp_l2tp_avp_list *peer_avps; /* avps in [IO]CCN */
! 199: u_int16_t session_id; /* session id */
! 200: u_int16_t peer_id; /* peer session id */
! 201: struct ppp_log *log; /* log */
! 202: ng_ID_t node_id; /* tee node id */
! 203: char hook[NG_HOOKSIZ]; /* session hook name */
! 204: void *link_cookie; /* opaque link cookie */
! 205: u_int16_t result; /* close result code */
! 206: u_int16_t error; /* close error code */
! 207: char *errmsg; /* close error msg */
! 208: struct pevent *reply_timer; /* reply timer */
! 209: struct pevent *notify_timer; /* link notify timer */
! 210: struct pevent *close_timer; /* close timer */
! 211: struct pevent *death_timer; /* death timer */
! 212: u_char link_responded; /* link notified up */
! 213: u_char peer_responded; /* got icrp from lns */
! 214: u_char dseq_required; /* data seq. req'd */
! 215: u_char link_notified; /* link notified down */
! 216: u_char peer_notified; /* peer notified down */
! 217: };
! 218:
! 219: /***
! 220:
! 221: Notes
! 222:
! 223: - "link_notified" means the link side has been notified that
! 224: the control connection or session has gone down.
! 225: - "peer_notified" means the peer has been notified that
! 226: the control connection or session has gone down.
! 227: - "link_responded" and "peer_responded" are only used for
! 228: outgoing calls when we are the LAC; they indicate acceptance
! 229: from the link side and the remote peer, respectively. Both
! 230: must be true before we send the OCCN.
! 231: - "sess->my_avps" are the AVP's included my ICRQ or OCRQ. In case
! 232: of ICRQ, these get overwritten by AVP's included in ICCN.
! 233: - "sess->peer_avps" are the AVPS included peer's ICCN or OCCN
! 234:
! 235: ***/
! 236:
! 237: /************************************************************************
! 238: INTERNAL FUNCTIONS
! 239: ************************************************************************/
! 240:
! 241: static int ppp_l2tp_ctrl_setup_1(struct ppp_l2tp_ctrl *ctrl,
! 242: struct ppp_l2tp_avp_ptrs *ptrs);
! 243: static int ppp_l2tp_ctrl_setup_2(struct ppp_l2tp_ctrl *ctrl,
! 244: struct ppp_l2tp_avp_ptrs *ptrs);
! 245: static void ppp_l2tp_ctrl_send(struct ppp_l2tp_ctrl *ctrl,
! 246: u_int16_t session_id, enum l2tp_msg_type msgtype,
! 247: const struct ppp_l2tp_avp_list *avps0);
! 248: static void ppp_l2tp_ctrl_check_reply(struct ppp_l2tp_ctrl *ctrl);
! 249: static void ppp_l2tp_ctrl_close(struct ppp_l2tp_ctrl *ctrl,
! 250: u_int16_t result, u_int16_t error, const char *errmsg);
! 251: static void ppp_l2tp_ctrl_death_start(struct ppp_l2tp_ctrl *ctrl);
! 252:
! 253: static struct ppp_l2tp_sess *ppp_l2tp_sess_create(struct ppp_l2tp_ctrl *ctrl,
! 254: enum l2tp_sess_orig orig, enum l2tp_sess_side side);
! 255: static void ppp_l2tp_sess_destroy(struct ppp_l2tp_sess **sessp);
! 256: static void ppp_l2tp_sess_close(struct ppp_l2tp_sess *sess,
! 257: u_int16_t result, u_int16_t error, const char *errmsg);
! 258: static int ppp_l2tp_sess_setup(struct ppp_l2tp_sess *sess);
! 259: static int ppp_l2tp_sess_check_liic(struct ppp_l2tp_sess *sess);
! 260: static void ppp_l2tp_sess_check_reply(struct ppp_l2tp_sess *sess);
! 261:
! 262: static l2tp_ctrl_msg_handler_t ppp_l2tp_handle_SCCRQ;
! 263: static l2tp_ctrl_msg_handler_t ppp_l2tp_handle_SCCRP;
! 264: static l2tp_ctrl_msg_handler_t ppp_l2tp_handle_SCCCN;
! 265: static l2tp_ctrl_msg_handler_t ppp_l2tp_handle_StopCCN;
! 266: static l2tp_ctrl_msg_handler_t ppp_l2tp_handle_HELLO;
! 267: static l2tp_ctrl_msg_handler_t ppp_l2tp_handle_OCRQ;
! 268: static l2tp_ctrl_msg_handler_t ppp_l2tp_handle_ICRQ;
! 269:
! 270: static l2tp_sess_msg_handler_t ppp_l2tp_handle_OCRP;
! 271: static l2tp_sess_msg_handler_t ppp_l2tp_handle_xCCN;
! 272: static l2tp_sess_msg_handler_t ppp_l2tp_handle_ICRP;
! 273: static l2tp_sess_msg_handler_t ppp_l2tp_handle_CDN;
! 274: static l2tp_sess_msg_handler_t ppp_l2tp_handle_WEN;
! 275: static l2tp_sess_msg_handler_t ppp_l2tp_handle_SLI;
! 276:
! 277: static pevent_handler_t ppp_l2tp_ctrl_event;
! 278: static pevent_handler_t ppp_l2tp_data_event;
! 279:
! 280: static pevent_handler_t ppp_l2tp_idle_timeout;
! 281: static pevent_handler_t ppp_l2tp_ctrl_do_close;
! 282: static pevent_handler_t ppp_l2tp_ctrl_death_timeout;
! 283:
! 284: static pevent_handler_t ppp_l2tp_sess_notify;
! 285: static pevent_handler_t ppp_l2tp_sess_do_close;
! 286: static pevent_handler_t ppp_l2tp_sess_death_timeout;
! 287:
! 288: static void ppp_l2tp_ctrl_dump(struct ppp_l2tp_ctrl *ctrl,
! 289: struct ppp_l2tp_avp_list *list, const char *fmt, ...)
! 290: __printflike(3, 4);
! 291: static const char *ppp_l2tp_ctrl_state_str(enum l2tp_ctrl_state state);
! 292: static const char *ppp_l2tp_sess_state_str(enum l2tp_ctrl_state state);
! 293: static const char *ppp_l2tp_sess_orig_str(enum l2tp_sess_orig orig);
! 294: static const char *ppp_l2tp_sess_side_str(enum l2tp_sess_side side);
! 295:
! 296: static ghash_hash_t ppp_l2tp_ctrl_hash;
! 297: static ghash_equal_t ppp_l2tp_ctrl_equal;
! 298:
! 299: static ghash_hash_t ppp_l2tp_sess_hash;
! 300: static ghash_equal_t ppp_l2tp_sess_equal;
! 301:
! 302: /************************************************************************
! 303: INTERNAL VARIABLES
! 304: ************************************************************************/
! 305:
! 306: /* Descriptors for each L2TP message type */
! 307: static const struct l2tp_msg_info ppp_l2tp_msg_info[] = {
! 308:
! 309: /* Control connection messages */
! 310: { "SCCRQ", SCCRQ, ppp_l2tp_handle_SCCRQ, NULL,
! 311: { CS_IDLE, -1 },
! 312: { -1 }, { -1 },
! 313: { AVP_PROTOCOL_VERSION, AVP_HOST_NAME,
! 314: AVP_FRAMING_CAPABILITIES, AVP_ASSIGNED_TUNNEL_ID, -1 } },
! 315: { "SCCRP", SCCRP, ppp_l2tp_handle_SCCRP, NULL,
! 316: { CS_WAIT_CTL_REPLY, -1 },
! 317: { -1 }, { -1 },
! 318: { AVP_PROTOCOL_VERSION, AVP_HOST_NAME,
! 319: AVP_FRAMING_CAPABILITIES, AVP_ASSIGNED_TUNNEL_ID, -1 } },
! 320: { "SCCCN", SCCCN, ppp_l2tp_handle_SCCCN, NULL,
! 321: { CS_WAIT_CTL_CONNECT, -1 },
! 322: { -1 }, { -1 },
! 323: { -1 } },
! 324: { "StopCCN", StopCCN, ppp_l2tp_handle_StopCCN, NULL,
! 325: { CS_IDLE, CS_WAIT_CTL_REPLY, CS_WAIT_CTL_CONNECT,
! 326: CS_ESTABLISHED, CS_DYING, -1 },
! 327: { -1 }, { -1 },
! 328: { AVP_ASSIGNED_TUNNEL_ID, AVP_RESULT_CODE, -1 } },
! 329: { "HELLO", HELLO, ppp_l2tp_handle_HELLO, NULL,
! 330: { CS_IDLE, CS_WAIT_CTL_REPLY, CS_WAIT_CTL_CONNECT,
! 331: CS_ESTABLISHED, CS_DYING, -1 },
! 332: { -1 }, { -1 },
! 333: { -1 } },
! 334: { "ICRQ", ICRQ, ppp_l2tp_handle_ICRQ, NULL,
! 335: { CS_ESTABLISHED, -1 },
! 336: { -1 }, { -1 },
! 337: { AVP_ASSIGNED_SESSION_ID, AVP_CALL_SERIAL_NUMBER, -1 } },
! 338: { "OCRQ", OCRQ, ppp_l2tp_handle_OCRQ, NULL,
! 339: { CS_ESTABLISHED, -1 },
! 340: { -1 }, { -1 },
! 341: { AVP_ASSIGNED_SESSION_ID, AVP_CALL_SERIAL_NUMBER,
! 342: AVP_MINIMUM_BPS, AVP_MAXIMUM_BPS, AVP_BEARER_TYPE,
! 343: AVP_FRAMING_TYPE, AVP_CALLED_NUMBER, -1 } },
! 344:
! 345: /* Session connection messages */
! 346: { "ICRP", ICRP, NULL, ppp_l2tp_handle_ICRP,
! 347: { SS_WAIT_REPLY, SS_DYING, -1 },
! 348: { ORIG_LOCAL, -1 }, { SIDE_LAC, -1 },
! 349: { AVP_ASSIGNED_SESSION_ID, -1 } },
! 350: { "OCRP", OCRP, NULL, ppp_l2tp_handle_OCRP,
! 351: { SS_WAIT_REPLY, SS_DYING, -1 },
! 352: { ORIG_LOCAL, -1 }, { SIDE_LNS, -1 },
! 353: { AVP_ASSIGNED_SESSION_ID, -1 } },
! 354: { "ICCN", ICCN, NULL, ppp_l2tp_handle_xCCN,
! 355: { SS_WAIT_CONNECT, SS_DYING, -1 },
! 356: { ORIG_REMOTE, -1 }, { SIDE_LNS, -1 },
! 357: { AVP_TX_CONNECT_SPEED, AVP_FRAMING_TYPE, -1 } },
! 358: { "OCCN", OCCN, NULL, ppp_l2tp_handle_xCCN,
! 359: { SS_WAIT_CONNECT, SS_DYING, -1 },
! 360: { ORIG_LOCAL, -1 }, { SIDE_LNS, -1 },
! 361: { AVP_TX_CONNECT_SPEED, AVP_FRAMING_TYPE, -1 } },
! 362: { "CDN", CDN, NULL, ppp_l2tp_handle_CDN,
! 363: { SS_WAIT_REPLY, SS_WAIT_CONNECT,
! 364: SS_WAIT_ANSWER, SS_ESTABLISHED, SS_DYING, -1 },
! 365: { ORIG_LOCAL, ORIG_REMOTE, -1 }, { SIDE_LAC, SIDE_LNS, -1 },
! 366: { AVP_RESULT_CODE, AVP_ASSIGNED_SESSION_ID, -1 } },
! 367: { "WEN", WEN, NULL, ppp_l2tp_handle_WEN,
! 368: { SS_ESTABLISHED, SS_DYING, -1 },
! 369: { ORIG_LOCAL, ORIG_REMOTE, -1 }, { SIDE_LNS, -1 },
! 370: { AVP_CALL_ERRORS, -1 } },
! 371: { "SLI", SLI, NULL, ppp_l2tp_handle_SLI,
! 372: { SS_ESTABLISHED, SS_DYING, -1 },
! 373: { ORIG_LOCAL, ORIG_REMOTE, -1 }, { SIDE_LAC, -1 },
! 374: { AVP_ACCM, -1 } },
! 375: { NULL }
! 376: };
! 377:
! 378: /* Descriptors for each AVP */
! 379:
! 380: #define AVP_ITEM(x,m,h,min,max) \
! 381: { #x, ppp_l2tp_avp_decode_ ## x, 0, AVP_ ## x, m, h, min, max }
! 382:
! 383: static const struct ppp_l2tp_avp_info ppp_l2tp_avp_info_list[] = {
! 384: AVP_ITEM(MESSAGE_TYPE, 0, 1, 2, 2),
! 385: AVP_ITEM(RANDOM_VECTOR, 0, 1, 0, AVP_MAX_LENGTH),
! 386: AVP_ITEM(RESULT_CODE, 0, 1, 2, AVP_MAX_LENGTH),
! 387: AVP_ITEM(PROTOCOL_VERSION, 0, 1, 2, 2),
! 388: AVP_ITEM(FRAMING_CAPABILITIES, 1, 1, 4, 4),
! 389: AVP_ITEM(BEARER_CAPABILITIES, 1, 1, 4, 4),
! 390: AVP_ITEM(TIE_BREAKER, 0, 0, 8, 8),
! 391: AVP_ITEM(FIRMWARE_REVISION, 1, 0, 2, 2),
! 392: AVP_ITEM(HOST_NAME, 0, 1, 1, AVP_MAX_LENGTH),
! 393: AVP_ITEM(VENDOR_NAME, 1, 0, 0, AVP_MAX_LENGTH),
! 394: AVP_ITEM(ASSIGNED_TUNNEL_ID, 1, 1, 2, 2),
! 395: AVP_ITEM(RECEIVE_WINDOW_SIZE, 0, 1, 2, 2),
! 396: AVP_ITEM(CHALLENGE, 1, 1, 0, AVP_MAX_LENGTH),
! 397: AVP_ITEM(CHALLENGE_RESPONSE, 1, 1, 16, 16),
! 398: AVP_ITEM(CAUSE_CODE, 0, 1, 3, AVP_MAX_LENGTH),
! 399: AVP_ITEM(ASSIGNED_SESSION_ID, 1, 1, 2, 2),
! 400: AVP_ITEM(CALL_SERIAL_NUMBER, 1, 1, 4, 4),
! 401: AVP_ITEM(MINIMUM_BPS, 1, 1, 4, 4),
! 402: AVP_ITEM(MAXIMUM_BPS, 1, 1, 4, 4),
! 403: AVP_ITEM(BEARER_TYPE, 1, 1, 4, 4),
! 404: AVP_ITEM(FRAMING_TYPE, 1, 1, 4, 4),
! 405: AVP_ITEM(CALLED_NUMBER, 1, 1, 0, AVP_MAX_LENGTH),
! 406: AVP_ITEM(CALLING_NUMBER, 1, 1, 0, AVP_MAX_LENGTH),
! 407: AVP_ITEM(SUB_ADDRESS, 1, 1, 0, AVP_MAX_LENGTH),
! 408: AVP_ITEM(TX_CONNECT_SPEED, 1, 1, 4, 4),
! 409: AVP_ITEM(RX_CONNECT_SPEED, 1, 0, 4, 4),
! 410: AVP_ITEM(PHYSICAL_CHANNEL_ID, 1, 0, 4, 4),
! 411: AVP_ITEM(PRIVATE_GROUP_ID, 1, 0, 0, AVP_MAX_LENGTH),
! 412: AVP_ITEM(SEQUENCING_REQUIRED, 0, 1, 0, 0),
! 413: AVP_ITEM(INITIAL_RECV_CONFREQ, 1, 0, 0, AVP_MAX_LENGTH),
! 414: AVP_ITEM(LAST_SENT_CONFREQ, 1, 0, 0, AVP_MAX_LENGTH),
! 415: AVP_ITEM(LAST_RECV_CONFREQ, 1, 0, 0, AVP_MAX_LENGTH),
! 416: AVP_ITEM(PROXY_AUTHEN_TYPE, 1, 0, 2, 2),
! 417: AVP_ITEM(PROXY_AUTHEN_NAME, 1, 0, 0, AVP_MAX_LENGTH),
! 418: AVP_ITEM(PROXY_AUTHEN_CHALLENGE,1, 0, 0, AVP_MAX_LENGTH),
! 419: AVP_ITEM(PROXY_AUTHEN_ID, 1, 0, 2, 2),
! 420: AVP_ITEM(PROXY_AUTHEN_RESPONSE, 1, 0, 0, AVP_MAX_LENGTH),
! 421: AVP_ITEM(CALL_ERRORS, 1, 1, 26, 26),
! 422: AVP_ITEM(ACCM, 1, 1, 10, 10),
! 423: { NULL, NULL, 0, 0, 0, 0, 0, 0 }
! 424: };
! 425:
! 426: /* All control connections */
! 427: struct ghash *ppp_l2tp_ctrls;
! 428:
! 429: /* Macros for logging */
! 430: #define CLOG(sev, fmt, args...) PPP_LOG(ctrl->log, sev, fmt , ## args)
! 431: #define SLOG(sev, fmt, args...) PPP_LOG(sess->log, sev, fmt , ## args)
! 432:
! 433: /************************************************************************
! 434: PUBLIC FUNCTIONS
! 435: ************************************************************************/
! 436:
! 437: /*
! 438: * Create a new control connection.
! 439: */
! 440: struct ppp_l2tp_ctrl *
! 441: ppp_l2tp_ctrl_create(struct pevent_ctx *ctx, pthread_mutex_t *mutex,
! 442: const struct ppp_l2tp_ctrl_cb *cb, struct ppp_log *log,
! 443: int initiate, u_int32_t peer_id, ng_ID_t *nodep, char *hook,
! 444: const struct ppp_l2tp_avp_list *avps, const void *secret, size_t seclen)
! 445: {
! 446: union {
! 447: u_char buf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)];
! 448: struct ng_mesg reply;
! 449: } repbuf;
! 450: struct ng_mesg *const reply = &repbuf.reply;
! 451: struct nodeinfo ninfo;
! 452: struct ppp_l2tp_ctrl *ctrl;
! 453: struct ngm_mkpeer mkpeer;
! 454: struct ppp_l2tp_avp *avp = NULL;
! 455: u_int16_t value16;
! 456: int index;
! 457:
! 458: /* Create global control structure hash table */
! 459: if (ppp_l2tp_ctrls == NULL
! 460: && (ppp_l2tp_ctrls = ghash_create(NULL, 0, 0, CTRL_MEM_TYPE,
! 461: ppp_l2tp_ctrl_hash, ppp_l2tp_ctrl_equal, NULL, NULL)) == NULL)
! 462: return (NULL);
! 463:
! 464: /* Create control connection */
! 465: if ((ctrl = MALLOC(CTRL_MEM_TYPE, sizeof(*ctrl))) == NULL)
! 466: return (NULL);
! 467: memset(ctrl, 0, sizeof(*ctrl));
! 468: ctrl->ctx = ctx;
! 469: ctrl->mutex = mutex;
! 470: ctrl->cb = cb;
! 471: ctrl->peer_id = peer_id;
! 472: ctrl->csock = -1;
! 473: ctrl->dsock = -1;
! 474: ctrl->log = log;
! 475:
! 476: /* Debugging */
! 477: CLOG(LOG_DEBUG, "%s: invoked", __FUNCTION__);
! 478:
! 479: /* Select an unused, non-zero local tunnel ID */
! 480: while (ctrl->config.tunnel_id == 0
! 481: || ghash_get(ppp_l2tp_ctrls, ctrl) != NULL)
! 482: ctrl->config.tunnel_id = random() & 0xffff;
! 483:
! 484: /* Add control structure to hash table */
! 485: if (ghash_put(ppp_l2tp_ctrls, ctrl) == -1)
! 486: goto fail;
! 487:
! 488: /* Copy shared secret, if any */
! 489: if (seclen > 0) {
! 490: if ((ctrl->secret = MALLOC(CTRL_MEM_TYPE, seclen)) == NULL)
! 491: goto fail;
! 492: memcpy(ctrl->secret, secret, seclen);
! 493: }
! 494: ctrl->seclen = seclen;
! 495:
! 496: /* Create sessions hash table */
! 497: if ((ctrl->sessions = ghash_create(NULL, 0, 0, CTRL_MEM_TYPE,
! 498: ppp_l2tp_sess_hash, ppp_l2tp_sess_equal, NULL, NULL)) == NULL)
! 499: goto fail;
! 500:
! 501: /* Create netgraph node */
! 502: if (NgMkSockNode(NULL, &ctrl->csock, &ctrl->dsock) == -1)
! 503: goto fail;
! 504: memset(&mkpeer, 0, sizeof(mkpeer));
! 505: strlcpy(mkpeer.type, NG_L2TP_NODE_TYPE, sizeof(mkpeer.type));
! 506: strlcpy(mkpeer.ourhook, NG_L2TP_HOOK_CTRL, sizeof(mkpeer.ourhook));
! 507: strlcpy(mkpeer.peerhook, NG_L2TP_HOOK_CTRL, sizeof(mkpeer.peerhook));
! 508: if (NgSendMsg(ctrl->csock, ".", NGM_GENERIC_COOKIE,
! 509: NGM_MKPEER, &mkpeer, sizeof(mkpeer)) == -1)
! 510: goto fail;
! 511:
! 512: /* Get l2tp node ID */
! 513: if (NgSendMsg(ctrl->csock, NG_L2TP_HOOK_CTRL,
! 514: NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) == -1)
! 515: goto fail;
! 516: if (NgRecvMsg(ctrl->csock, reply, sizeof(repbuf), NULL) == -1)
! 517: goto fail;
! 518: memcpy(&ninfo, reply->data, sizeof(ninfo));
! 519: ctrl->node_id = ninfo.id;
! 520: snprintf(ctrl->path, sizeof(ctrl->path),
! 521: "[%lx]:", (u_long)ctrl->node_id);
! 522:
! 523: /* Configure netgraph node with initial configuration */
! 524: ctrl->config.enabled = 1;
! 525: ctrl->config.peer_win = 1; /* we increase this later */
! 526: ctrl->config.rexmit_max = L2TP_REXMIT_MAX;
! 527: ctrl->config.rexmit_max_to = L2TP_REXMIT_MAX_TO;
! 528: if (NgSendMsg(ctrl->csock, NG_L2TP_HOOK_CTRL, NGM_L2TP_COOKIE,
! 529: NGM_L2TP_SET_CONFIG, &ctrl->config, sizeof(ctrl->config)) == -1)
! 530: goto fail;
! 531:
! 532: /* Listen for control messages and control packets */
! 533: if (pevent_register(ctrl->ctx, &ctrl->ctrl_event, PEVENT_RECURRING,
! 534: ctrl->mutex, ppp_l2tp_ctrl_event, ctrl, PEVENT_READ,
! 535: ctrl->csock) == -1)
! 536: goto fail;
! 537: if (pevent_register(ctrl->ctx, &ctrl->data_event, PEVENT_RECURRING,
! 538: ctrl->mutex, ppp_l2tp_data_event, ctrl, PEVENT_READ,
! 539: ctrl->dsock) == -1)
! 540: goto fail;
! 541:
! 542: /* Copy initial AVP list */
! 543: ctrl->avps = (avps == NULL) ?
! 544: ppp_l2tp_avp_list_create() :
! 545: ppp_l2tp_avp_list_copy(avps);
! 546: if (ctrl->avps == NULL)
! 547: goto fail;
! 548:
! 549: /* Add required AVP's */
! 550: if ((index = ppp_l2tp_avp_list_find(ctrl->avps,
! 551: 0, AVP_PROTOCOL_VERSION)) == -1) {
! 552: const u_char pv[] = { L2TP_PROTO_VERS, L2TP_PROTO_REV };
! 553:
! 554: if (ppp_l2tp_avp_list_append(ctrl->avps, 1,
! 555: 0, AVP_PROTOCOL_VERSION, pv, sizeof(pv)) == -1)
! 556: goto fail;
! 557: }
! 558: if ((index = ppp_l2tp_avp_list_find(ctrl->avps,
! 559: 0, AVP_HOST_NAME)) == -1) {
! 560: char hostname[MAXHOSTNAMELEN + 1];
! 561:
! 562: (void)gethostname(hostname, sizeof(hostname) - 1);
! 563: hostname[sizeof(hostname) - 1] = '\0';
! 564: if (ppp_l2tp_avp_list_append(ctrl->avps, 1,
! 565: 0, AVP_HOST_NAME, hostname, strlen(hostname)) == -1)
! 566: goto fail;
! 567: }
! 568: if ((index = ppp_l2tp_avp_list_find(ctrl->avps,
! 569: 0, AVP_FRAMING_CAPABILITIES)) == -1) {
! 570: const u_int32_t value = htonl(L2TP_FRAMING_SYNC);
! 571:
! 572: if (ppp_l2tp_avp_list_append(ctrl->avps, 1,
! 573: 0, AVP_FRAMING_CAPABILITIES, &value, sizeof(value)) == -1)
! 574: goto fail;
! 575: }
! 576: if ((index = ppp_l2tp_avp_list_find(ctrl->avps,
! 577: 0, AVP_ASSIGNED_TUNNEL_ID)) != -1)
! 578: ppp_l2tp_avp_list_remove(ctrl->avps, index);
! 579: value16 = htons(ctrl->config.tunnel_id);
! 580: if (ppp_l2tp_avp_list_append(ctrl->avps, 1,
! 581: 0, AVP_ASSIGNED_TUNNEL_ID, &value16, sizeof(value16)) == -1)
! 582: goto fail;
! 583: if (ctrl->secret == NULL) {
! 584: if ((index = ppp_l2tp_avp_list_find(ctrl->avps,
! 585: 0, AVP_CHALLENGE)) != -1)
! 586: ppp_l2tp_avp_list_remove(ctrl->avps, index);
! 587: } else if ((index = ppp_l2tp_avp_list_find(ctrl->avps,
! 588: 0, AVP_CHALLENGE)) == -1) {
! 589: u_int32_t cbuf[(L2TP_CHALLENGE_LEN + 3) / 4];
! 590: int i;
! 591:
! 592: for (i = 0; i < sizeof(cbuf) / sizeof(*cbuf); i++)
! 593: cbuf[i] = random();
! 594: if (ppp_l2tp_avp_list_append(ctrl->avps, 1,
! 595: 0, AVP_CHALLENGE, &cbuf, sizeof(cbuf)) == -1)
! 596: goto fail;
! 597: }
! 598:
! 599: /* Initiate connection if we're the initiator */
! 600: if (initiate) {
! 601: ppp_l2tp_ctrl_send(ctrl, 0, SCCRQ, ctrl->avps);
! 602: ctrl->state = CS_WAIT_CTL_REPLY;
! 603: } else
! 604: ctrl->state = CS_IDLE; /* wait for peer's sccrq */
! 605:
! 606: /* Expect some sort of reply */
! 607: ppp_l2tp_ctrl_check_reply(ctrl);
! 608:
! 609: /* Done */
! 610: *nodep = ctrl->node_id;
! 611: strlcpy(hook, NG_L2TP_HOOK_LOWER, NG_HOOKSIZ);
! 612: return (ctrl);
! 613:
! 614: fail:
! 615: /* Clean up after failure */
! 616: ppp_l2tp_avp_destroy(&avp);
! 617: if (ctrl->csock >= 0)
! 618: (void)close(ctrl->csock); /* l2tp node will go away too */
! 619: if (ctrl->dsock >= 0)
! 620: (void)close(ctrl->dsock);
! 621: pevent_unregister(&ctrl->reply_timer);
! 622: pevent_unregister(&ctrl->ctrl_event);
! 623: pevent_unregister(&ctrl->data_event);
! 624: ppp_l2tp_avp_list_destroy(&ctrl->avps);
! 625: ghash_remove(ppp_l2tp_ctrls, ctrl);
! 626: ghash_destroy(&ctrl->sessions);
! 627: FREE(CTRL_MEM_TYPE, ctrl->secret);
! 628: FREE(CTRL_MEM_TYPE, ctrl);
! 629: if (ppp_l2tp_ctrls != NULL && ghash_size(ppp_l2tp_ctrls) == 0)
! 630: ghash_destroy(&ppp_l2tp_ctrls);
! 631: return (NULL);
! 632: }
! 633:
! 634: /*
! 635: * Shutdown a control connection.
! 636: */
! 637: void
! 638: ppp_l2tp_ctrl_shutdown(struct ppp_l2tp_ctrl *ctrl,
! 639: u_int16_t result, u_int16_t error, const char *errmsg)
! 640: {
! 641: /* Debugging */
! 642: CLOG(LOG_DEBUG, "%s: invoked, ctrl=%p errmsg=\"%s\"",
! 643: __FUNCTION__, ctrl, errmsg);
! 644:
! 645: /* Close control connection */
! 646: ppp_l2tp_ctrl_close(ctrl, result, error, errmsg);
! 647: }
! 648:
! 649: /*
! 650: * Initiate a new session.
! 651: */
! 652: struct ppp_l2tp_sess *
! 653: ppp_l2tp_initiate(struct ppp_l2tp_ctrl *ctrl, int out,
! 654: const struct ppp_l2tp_avp_list *avps)
! 655: {
! 656: struct ppp_l2tp_sess *sess;
! 657: u_int32_t value32;
! 658: int index;
! 659: int i;
! 660:
! 661: /* Debugging */
! 662: CLOG(LOG_DEBUG, "%s: invoked, ctrl=%p out=%d", __FUNCTION__, ctrl);
! 663:
! 664: /* Check control connection */
! 665: /* XXX add support for sessions waiting for open ctrl conection */
! 666: if (ctrl->state != CS_ESTABLISHED) {
! 667: errno = ENXIO;
! 668: return (NULL);
! 669: }
! 670:
! 671: /* Verify peer supports outgoing calls */
! 672: if (out
! 673: && (ctrl->peer_bearer
! 674: & (L2TP_BEARER_DIGITAL|L2TP_BEARER_ANALOG)) == 0) {
! 675: errno = EOPNOTSUPP;
! 676: return (NULL);
! 677: }
! 678:
! 679: /* Create new session */
! 680: if ((sess = ppp_l2tp_sess_create(ctrl,
! 681: ORIG_LOCAL, out ? SIDE_LNS : SIDE_LAC)) == NULL)
! 682: return (NULL);
! 683:
! 684: /* Copy AVP's supplied by caller */
! 685: for (i = 0; i < avps->length; i++) {
! 686: const struct ppp_l2tp_avp *const avp = &avps->avps[i];
! 687:
! 688: if (ppp_l2tp_avp_list_append(sess->my_avps,
! 689: avp->mandatory, avp->vendor, avp->type,
! 690: avp->value, avp->vlen) == -1)
! 691: goto fail;
! 692: }
! 693:
! 694: /* Add other AVP's required by OCRQ */
! 695: if (!out)
! 696: goto send_msg;
! 697: if ((index = ppp_l2tp_avp_list_find(sess->my_avps,
! 698: 0, AVP_MINIMUM_BPS)) == -1) {
! 699: value32 = htonl(0);
! 700: if (ppp_l2tp_avp_list_append(sess->my_avps, 1,
! 701: 0, AVP_MINIMUM_BPS, &value32, sizeof(value32)) == -1)
! 702: goto fail;
! 703: }
! 704: if ((index = ppp_l2tp_avp_list_find(sess->my_avps,
! 705: 0, AVP_MAXIMUM_BPS)) == -1) {
! 706: value32 = htonl(0x7fffffff);
! 707: if (ppp_l2tp_avp_list_append(sess->my_avps, 1,
! 708: 0, AVP_MAXIMUM_BPS, &value32, sizeof(value32)) == -1)
! 709: goto fail;
! 710: }
! 711: if ((index = ppp_l2tp_avp_list_find(sess->my_avps,
! 712: 0, AVP_FRAMING_TYPE)) == -1) {
! 713: value32 = (ctrl->peer_framing
! 714: & (L2TP_FRAMING_SYNC|L2TP_FRAMING_SYNC));
! 715: value32 = htonl(value32);
! 716: if (ppp_l2tp_avp_list_append(sess->my_avps, 1,
! 717: 0, AVP_FRAMING_TYPE, &value32, sizeof(value32)) == -1)
! 718: goto fail;
! 719: }
! 720: if ((index = ppp_l2tp_avp_list_find(sess->my_avps,
! 721: 0, AVP_CALLED_NUMBER)) == -1) {
! 722: if (ppp_l2tp_avp_list_append(sess->my_avps, 1,
! 723: 0, AVP_CALLED_NUMBER, NULL, 0) == -1)
! 724: goto fail;
! 725: }
! 726:
! 727: send_msg:
! 728: /* Send request to peer */
! 729: ppp_l2tp_ctrl_send(ctrl, 0, out ? OCRQ : ICRQ, sess->my_avps);
! 730:
! 731: /* Await reply from peer */
! 732: ppp_l2tp_sess_check_reply(sess);
! 733: return (sess);
! 734:
! 735: fail:
! 736: /* Clean up after failure */
! 737: ppp_l2tp_sess_destroy(&sess);
! 738: return (NULL);
! 739: }
! 740:
! 741: /*
! 742: * Report successful connection of a remotely initiated outgoing call
! 743: * or a locally initiated incoming call.
! 744: */
! 745: int
! 746: ppp_l2tp_connected(struct ppp_l2tp_sess *sess,
! 747: const struct ppp_l2tp_avp_list *avps)
! 748: {
! 749: struct ppp_l2tp_ctrl *const ctrl = sess->ctrl;
! 750: u_int32_t value32;
! 751: int i;
! 752:
! 753: /* Debugging */
! 754: SLOG(LOG_DEBUG, "%s: invoked, sess=%p", __FUNCTION__, sess);
! 755:
! 756: /* Check control connection */
! 757: if (ctrl->state != CS_ESTABLISHED) {
! 758: SLOG(LOG_ERR, "inappropriate call to %s", __FUNCTION__);
! 759: errno = ENXIO;
! 760: return (-1);
! 761: }
! 762:
! 763: /* Check session state */
! 764: if (sess->side != SIDE_LAC
! 765: || !((sess->orig == ORIG_REMOTE && sess->state == SS_WAIT_ANSWER)
! 766: || (sess->orig == ORIG_LOCAL && sess->state == SS_WAIT_REPLY))) {
! 767: SLOG(LOG_ERR, "inappropriate call to %s", __FUNCTION__);
! 768: errno = ENOTTY;
! 769: return (-1);
! 770: }
! 771:
! 772: /* Copy AVP's supplied by caller */
! 773: while (sess->my_avps->length > 0)
! 774: ppp_l2tp_avp_list_remove(sess->my_avps, 0);
! 775: if (avps != NULL) {
! 776: for (i = 0; i < avps->length; i++) {
! 777: struct ppp_l2tp_avp *const avp = &avps->avps[i];
! 778:
! 779: if (ppp_l2tp_avp_list_append(sess->my_avps,
! 780: avp->mandatory, avp->vendor, avp->type,
! 781: avp->value, avp->vlen) == -1)
! 782: goto fail;
! 783: }
! 784: }
! 785:
! 786: /* Add other AVP's required by ICCN and OCCN */
! 787: if (ppp_l2tp_avp_list_find(sess->my_avps,
! 788: 0, AVP_TX_CONNECT_SPEED) == -1) {
! 789: value32 = htonl(L2TP_CONNECT_SPEED);
! 790: if (ppp_l2tp_avp_list_append(sess->my_avps, 1,
! 791: 0, AVP_TX_CONNECT_SPEED, &value32, sizeof(value32)) == -1)
! 792: goto fail;
! 793: }
! 794: if (ppp_l2tp_avp_list_find(sess->my_avps,
! 795: 0, AVP_FRAMING_TYPE) == -1) {
! 796: value32 = htonl(L2TP_FRAMING_TYPE);
! 797: if (ppp_l2tp_avp_list_append(sess->my_avps, 1,
! 798: 0, AVP_FRAMING_TYPE, &value32, sizeof(value32)) == -1)
! 799: goto fail;
! 800: }
! 801:
! 802: /* Handle incoming or outgoing call */
! 803: if (sess->orig == ORIG_LOCAL) {
! 804: sess->link_responded = 1;
! 805: if (ppp_l2tp_sess_check_liic(sess) == -1)
! 806: goto fail;
! 807: } else {
! 808: if (ppp_l2tp_sess_setup(sess) == -1)
! 809: goto fail;
! 810: ppp_l2tp_ctrl_send(ctrl, sess->peer_id, OCCN, sess->my_avps);
! 811: }
! 812:
! 813: /* Done */
! 814: return (0);
! 815:
! 816: fail:
! 817: /* Clean up after failure */
! 818: ppp_l2tp_sess_close(sess, L2TP_RESULT_ERROR,
! 819: L2TP_ERROR_GENERIC, strerror(errno));
! 820: return (-1);
! 821: }
! 822:
! 823: /*
! 824: * Terminate a session.
! 825: */
! 826: void
! 827: ppp_l2tp_terminate(struct ppp_l2tp_sess *sess,
! 828: u_int16_t result, u_int16_t error, const char *errmsg)
! 829: {
! 830: struct ppp_l2tp_ctrl *const ctrl = sess->ctrl;
! 831:
! 832: /* Debugging */
! 833: SLOG(LOG_DEBUG, "%s: invoked, sess=%p errmsg=\"%s\"",
! 834: __FUNCTION__, sess, errmsg != NULL ? errmsg : "");
! 835:
! 836: /* Check control connection state */
! 837: if (ctrl->state == CS_DYING) {
! 838: sess->link_notified = 1;
! 839: sess->link_cookie = NULL;
! 840: return;
! 841: }
! 842: if (ctrl->state != CS_ESTABLISHED) {
! 843: SLOG(LOG_ERR, "inappropriate call to %s", __FUNCTION__);
! 844: return;
! 845: }
! 846:
! 847: /* Close connection */
! 848: sess->link_notified = 1;
! 849: sess->link_cookie = NULL;
! 850: ppp_l2tp_sess_close(sess, result, error, errmsg);
! 851: }
! 852:
! 853: /*
! 854: * Get the link side cookie corresponding to a control connection.
! 855: */
! 856: void *
! 857: ppp_l2tp_ctrl_get_cookie(struct ppp_l2tp_ctrl *ctrl)
! 858: {
! 859: return (ctrl->link_cookie);
! 860: }
! 861:
! 862: /*
! 863: * Set the link side cookie corresponding to a control connection.
! 864: */
! 865: void
! 866: ppp_l2tp_ctrl_set_cookie(struct ppp_l2tp_ctrl *ctrl, void *cookie)
! 867: {
! 868: ctrl->link_cookie = cookie;
! 869: }
! 870:
! 871: /*
! 872: * Get the link side cookie corresponding to a session.
! 873: */
! 874: void *
! 875: ppp_l2tp_sess_get_cookie(struct ppp_l2tp_sess *sess)
! 876: {
! 877: return (sess->link_cookie);
! 878: }
! 879:
! 880: /*
! 881: * Set the link side cookie corresponding to a session.
! 882: */
! 883: void
! 884: ppp_l2tp_sess_set_cookie(struct ppp_l2tp_sess *sess, void *cookie)
! 885: {
! 886: sess->link_cookie = cookie;
! 887: }
! 888:
! 889: /*
! 890: * Get the node path and hook name for the hook that corresponds
! 891: * to a control connection's L2TP frames.
! 892: */
! 893: void
! 894: ppp_l2tp_ctrl_get_hook(struct ppp_l2tp_ctrl *ctrl,
! 895: ng_ID_t *nodep, const char **hookp)
! 896: {
! 897: if (nodep != NULL)
! 898: *nodep = ctrl->node_id;
! 899: if (hookp != NULL)
! 900: *hookp = NG_L2TP_HOOK_LOWER;
! 901: }
! 902:
! 903: /*
! 904: * Get the node path and hook name for the hook that corresponds
! 905: * to a session's data packets.
! 906: */
! 907: void
! 908: ppp_l2tp_sess_get_hook(struct ppp_l2tp_sess *sess,
! 909: ng_ID_t *nodep, const char **hookp)
! 910: {
! 911: if (nodep != NULL)
! 912: *nodep = sess->node_id;
! 913: if (hookp != NULL)
! 914: *hookp = NG_TEE_HOOK_RIGHT;
! 915: }
! 916:
! 917: /************************************************************************
! 918: INTERNAL FUNCTIONS: CONTROL CONNECTION
! 919: ************************************************************************/
! 920:
! 921: /*
! 922: * Extract peer information from AVP's contained in a SCCRQ or SCCRP.
! 923: * This is the first part of tunnel setup.
! 924: */
! 925: static int
! 926: ppp_l2tp_ctrl_setup_1(struct ppp_l2tp_ctrl *ctrl,
! 927: struct ppp_l2tp_avp_ptrs *ptrs)
! 928: {
! 929: /* Log */
! 930: CLOG(LOG_INFO, "connected to \"%s\", version=%u.%u",
! 931: ptrs->hostname->hostname, ptrs->protocol->version,
! 932: ptrs->protocol->revision);
! 933:
! 934: /* Get peer's tunnel ID */
! 935: ctrl->config.peer_id = ptrs->tunnelid->id;
! 936:
! 937: /* Get peer's receive window size */
! 938: ctrl->config.peer_win = L2TP_DEFAULT_PEER_WIN;
! 939: if (ptrs->winsize != NULL) {
! 940: if (ptrs->winsize->size == 0)
! 941: CLOG(LOG_WARNING, "ignoring zero recv window size AVP");
! 942: else
! 943: ctrl->config.peer_win = ptrs->winsize->size;
! 944: }
! 945:
! 946: /* Get peer's bearer and framing types */
! 947: if (ptrs->bearercap != NULL) {
! 948: if (ptrs->bearercap->digital)
! 949: ctrl->peer_bearer |= L2TP_BEARER_DIGITAL;
! 950: if (ptrs->bearercap->analog)
! 951: ctrl->peer_bearer |= L2TP_BEARER_ANALOG;
! 952: }
! 953: if (ptrs->framingcap->sync)
! 954: ctrl->peer_framing |= L2TP_FRAMING_SYNC;
! 955: if (ptrs->framingcap->async)
! 956: ctrl->peer_framing |= L2TP_FRAMING_ASYNC;
! 957:
! 958: /* Update netgraph node configuration */
! 959: if (NgSendMsg(ctrl->csock, NG_L2TP_HOOK_CTRL, NGM_L2TP_COOKIE,
! 960: NGM_L2TP_SET_CONFIG, &ctrl->config, sizeof(ctrl->config)) == -1)
! 961: return (-1);
! 962:
! 963: /* See if there is a challenge AVP */
! 964: if (ptrs->challenge != NULL) {
! 965:
! 966: /* Make sure response was included */
! 967: if (ctrl->secret == NULL) {
! 968: CLOG(LOG_WARNING, "tunnel challenge received but"
! 969: " no shared secret is configured");
! 970: ppp_l2tp_ctrl_close(ctrl,
! 971: L2TP_RESULT_NOT_AUTH, 0, NULL);
! 972: return (0);
! 973: }
! 974:
! 975: /* Compute challenge response XXX unimplemented */
! 976: CLOG(LOG_WARNING, "tunnel challenge/response unimplemented");
! 977: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_NOT_AUTH, 0, NULL);
! 978: return (0);
! 979: }
! 980:
! 981: /* Done */
! 982: return (0);
! 983: }
! 984:
! 985: /*
! 986: * Extract peer information from AVP's contained in a SCCRP or SCCCN.
! 987: * This is the second part of tunnel setup.
! 988: */
! 989: static int
! 990: ppp_l2tp_ctrl_setup_2(struct ppp_l2tp_ctrl *ctrl,
! 991: struct ppp_l2tp_avp_ptrs *ptrs)
! 992: {
! 993: /* Peer now knows our tunnel ID */
! 994: ctrl->config.match_id = 1;
! 995: if (NgSendMsg(ctrl->csock, NG_L2TP_HOOK_CTRL, NGM_L2TP_COOKIE,
! 996: NGM_L2TP_SET_CONFIG, &ctrl->config, sizeof(ctrl->config)) == -1)
! 997: return (-1);
! 998:
! 999: /* Verify peer's challenge response */
! 1000: if (ctrl->secret != NULL) {
! 1001:
! 1002: /* Make sure response was included */
! 1003: if (ptrs->challengresp == NULL) {
! 1004: CLOG(LOG_WARNING, "SCCRP lacks challenge response");
! 1005: ppp_l2tp_ctrl_close(ctrl,
! 1006: L2TP_RESULT_NOT_AUTH, 0, NULL);
! 1007: return (0);
! 1008: }
! 1009:
! 1010: /* Validate challenge response XXX unimplemented */
! 1011: CLOG(LOG_WARNING, "tunnel challenge/response unimplemented");
! 1012: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_NOT_AUTH, 0, NULL);
! 1013: return (0);
! 1014: }
! 1015:
! 1016: /* Done */
! 1017: return (0);
! 1018: }
! 1019:
! 1020: /*
! 1021: * Create a new session.
! 1022: */
! 1023: static struct ppp_l2tp_sess *
! 1024: ppp_l2tp_sess_create(struct ppp_l2tp_ctrl *ctrl,
! 1025: enum l2tp_sess_orig orig, enum l2tp_sess_side side)
! 1026: {
! 1027: struct ppp_l2tp_sess *sess = NULL;
! 1028: u_int16_t value16;
! 1029: u_int32_t value32;
! 1030:
! 1031: /* Create new session object */
! 1032: if ((sess = MALLOC(SESS_MEM_TYPE, sizeof(*sess))) == NULL) {
! 1033: CLOG(LOG_ERR, "error creating session: %m");
! 1034: return (NULL);
! 1035: }
! 1036: memset(sess, 0, sizeof(*sess));
! 1037: sess->ctrl = ctrl;
! 1038: sess->orig = orig;
! 1039: sess->side = side;
! 1040: sess->serial = ctrl->serial++;
! 1041: sess->state = (orig == ORIG_LOCAL) ? SS_WAIT_REPLY :
! 1042: (side == SIDE_LNS) ? SS_WAIT_CONNECT : SS_WAIT_ANSWER;
! 1043: if ((sess->log = ppp_log_prefix(ctrl->log,
! 1044: "call #%u: ", sess->serial)) == NULL) {
! 1045: FREE(SESS_MEM_TYPE, sess);
! 1046: return (NULL);
! 1047: }
! 1048:
! 1049: /* Get unique session ID */
! 1050: while (sess->config.session_id == 0
! 1051: || ghash_get(ctrl->sessions, sess) != NULL)
! 1052: sess->config.session_id = random() & 0xffff;
! 1053: snprintf(sess->hook, sizeof(sess->hook),
! 1054: NG_L2TP_HOOK_SESSION_F, sess->config.session_id);
! 1055:
! 1056: /* Add to control connection hash table */
! 1057: ctrl->active_sessions++;
! 1058: if (ghash_put(ctrl->sessions, sess) == -1) {
! 1059: ppp_l2tp_sess_destroy(&sess);
! 1060: return (NULL);
! 1061: }
! 1062:
! 1063: /* Create session AVP list to send to peer */
! 1064: if ((sess->my_avps = ppp_l2tp_avp_list_create()) == NULL) {
! 1065: ppp_l2tp_sess_destroy(&sess);
! 1066: return (NULL);
! 1067: }
! 1068:
! 1069: /* Add assigned session ID AVP */
! 1070: value16 = htons(sess->config.session_id);
! 1071: if (ppp_l2tp_avp_list_append(sess->my_avps, 1,
! 1072: 0, AVP_ASSIGNED_SESSION_ID, &value16, sizeof(value16)) == -1) {
! 1073: ppp_l2tp_sess_destroy(&sess);
! 1074: return (NULL);
! 1075: }
! 1076:
! 1077: /* Add call serial number AVP */
! 1078: value32 = htonl(sess->serial);
! 1079: if (ppp_l2tp_avp_list_append(sess->my_avps, 1,
! 1080: 0, AVP_CALL_SERIAL_NUMBER, &value32, sizeof(value32)) == -1) {
! 1081: ppp_l2tp_sess_destroy(&sess);
! 1082: return (NULL);
! 1083: }
! 1084:
! 1085: /* Done */
! 1086: CLOG(LOG_DEBUG, "created new session #%u id 0x%04x orig=%s side=%s"
! 1087: " state=%s", sess->serial, sess->config.session_id,
! 1088: ppp_l2tp_sess_orig_str(sess->orig),
! 1089: ppp_l2tp_sess_side_str(sess->side),
! 1090: ppp_l2tp_sess_state_str(sess->state));
! 1091: return (sess);
! 1092: }
! 1093:
! 1094: /*
! 1095: * Send a control message.
! 1096: */
! 1097: static void
! 1098: ppp_l2tp_ctrl_send(struct ppp_l2tp_ctrl *ctrl, u_int16_t session_id,
! 1099: enum l2tp_msg_type msgtype, const struct ppp_l2tp_avp_list *avps0)
! 1100: {
! 1101: struct ppp_l2tp_avp_list *avps = NULL;
! 1102: struct ppp_l2tp_avp *avp = NULL;
! 1103: u_char *data = NULL;
! 1104: u_int16_t value;
! 1105: int index;
! 1106: int len;
! 1107:
! 1108: /* Copy AVP list */
! 1109: avps = (avps0 == NULL) ? ppp_l2tp_avp_list_create()
! 1110: : ppp_l2tp_avp_list_copy(avps0);
! 1111: if (avps == NULL)
! 1112: goto fail;
! 1113:
! 1114: /* Remove any message type AVP in the way */
! 1115: if ((index = ppp_l2tp_avp_list_find(avps, 0, AVP_MESSAGE_TYPE)) != -1)
! 1116: ppp_l2tp_avp_list_remove(avps, index);
! 1117:
! 1118: /* Add message type AVP as first in the list */
! 1119: value = htons(msgtype);
! 1120: if ((avp = ppp_l2tp_avp_create(1, 0,
! 1121: AVP_MESSAGE_TYPE, &value, sizeof(value))) == NULL)
! 1122: goto fail;
! 1123: if (ppp_l2tp_avp_list_insert(avps, &avp, 0) == -1)
! 1124: goto fail;
! 1125:
! 1126: /* Encoded AVP's into a packet */
! 1127: if ((len = ppp_l2tp_avp_pack(ppp_l2tp_avp_info_list,
! 1128: avps, ctrl->secret, ctrl->seclen, NULL)) == -1)
! 1129: goto fail;
! 1130: if ((data = MALLOC(TYPED_MEM_TEMP, 2 + len)) == NULL)
! 1131: goto fail;
! 1132: session_id = htons(session_id);
! 1133: memcpy(data, &session_id, 2);
! 1134: (void)ppp_l2tp_avp_pack(ppp_l2tp_avp_info_list,
! 1135: avps, ctrl->secret, ctrl->seclen, data + 2);
! 1136:
! 1137: /* Write packet */
! 1138: if (session_id == 0)
! 1139: ppp_l2tp_ctrl_dump(ctrl, avps, "XMIT: ");
! 1140: else {
! 1141: ppp_l2tp_ctrl_dump(ctrl, avps, "XMIT(0x%04x): ",
! 1142: ntohs(session_id));
! 1143: }
! 1144: if (NgSendData(ctrl->dsock, NG_L2TP_HOOK_CTRL, data, 2 + len) == -1)
! 1145: goto fail;
! 1146:
! 1147: /* Done */
! 1148: goto done;
! 1149:
! 1150: fail:
! 1151: /* Close up shop */
! 1152: CLOG(LOG_ERR, "error sending ctrl packet: %m");
! 1153: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_ERROR,
! 1154: L2TP_ERROR_GENERIC, strerror(errno));
! 1155:
! 1156: done:
! 1157: /* Clean up */
! 1158: ppp_l2tp_avp_destroy(&avp);
! 1159: ppp_l2tp_avp_list_destroy(&avps);
! 1160: FREE(TYPED_MEM_TEMP, data);
! 1161: }
! 1162:
! 1163: /*
! 1164: * Close a control connection gracefully, after the next context switch.
! 1165: */
! 1166: static void
! 1167: ppp_l2tp_ctrl_close(struct ppp_l2tp_ctrl *ctrl,
! 1168: u_int16_t result, u_int16_t error, const char *errmsg)
! 1169: {
! 1170: /* Sanity check */
! 1171: if (ctrl->state == CS_DYING)
! 1172: return;
! 1173: ctrl->state = CS_DYING;
! 1174:
! 1175: /* Save result code and error string */
! 1176: ctrl->result = result;
! 1177: ctrl->error = error;
! 1178: FREE(CTRL_MEM_TYPE, ctrl->errmsg);
! 1179: ctrl->errmsg = (errmsg == NULL) ? NULL : STRDUP(CTRL_MEM_TYPE, errmsg);
! 1180:
! 1181: /* Notify peer if necessary */
! 1182: if (!ctrl->peer_notified) {
! 1183: struct ppp_l2tp_avp_list *avps = NULL;
! 1184: const size_t elen = (ctrl->errmsg == NULL) ?
! 1185: 0 : strlen(ctrl->errmsg);
! 1186: struct ppp_l2tp_sess *sess;
! 1187: struct ghash_walk walk;
! 1188: u_char *rbuf = NULL;
! 1189: u_int16_t value16;
! 1190:
! 1191: /* Create AVP list */
! 1192: ctrl->peer_notified = 1;
! 1193: if ((avps = ppp_l2tp_avp_list_create()) == NULL) {
! 1194: CLOG(LOG_ERR, "%s: %m", "ppp_l2tp_avp_list_create");
! 1195: goto notify_done;
! 1196: }
! 1197:
! 1198: /* Add assigned tunnel ID AVP */
! 1199: value16 = htons(ctrl->config.tunnel_id);
! 1200: if (ppp_l2tp_avp_list_append(avps, 1, 0,
! 1201: AVP_ASSIGNED_TUNNEL_ID, &value16, sizeof(value16)) == -1) {
! 1202: CLOG(LOG_ERR, "%s: %m", "ppp_l2tp_avp_list_append");
! 1203: goto notify_done;
! 1204: }
! 1205:
! 1206: /* Add result code AVP */
! 1207: if ((rbuf = MALLOC(TYPED_MEM_TEMP, 4 + elen)) == NULL)
! 1208: goto notify_done;
! 1209: value16 = htons(ctrl->result);
! 1210: memcpy(rbuf, &value16, sizeof(value16));
! 1211: value16 = htons(ctrl->error);
! 1212: memcpy(rbuf + 2, &value16, sizeof(value16));
! 1213: memcpy(rbuf + 4, ctrl->errmsg, elen);
! 1214: if (ppp_l2tp_avp_list_append(avps, 1, 0, AVP_RESULT_CODE,
! 1215: rbuf, 4 + elen) == -1) {
! 1216: CLOG(LOG_ERR, "%s: %m", "ppp_l2tp_avp_list_append");
! 1217: goto notify_done;
! 1218: }
! 1219:
! 1220: /* Send StopCCN */
! 1221: ppp_l2tp_ctrl_send(ctrl, 0, StopCCN, avps);
! 1222:
! 1223: /* StopCCN implies closing all sessions */
! 1224: ghash_walk_init(ctrl->sessions, &walk);
! 1225: while ((sess = ghash_walk_next(ctrl->sessions, &walk)) != NULL)
! 1226: sess->peer_notified = 1;
! 1227:
! 1228: notify_done:
! 1229: /* Clean up */
! 1230: ppp_l2tp_avp_list_destroy(&avps);
! 1231: FREE(TYPED_MEM_TEMP, rbuf);
! 1232: }
! 1233:
! 1234: /* Stop all timers */
! 1235: pevent_unregister(&ctrl->idle_timer);
! 1236: pevent_unregister(&ctrl->reply_timer);
! 1237: pevent_unregister(&ctrl->close_timer);
! 1238: pevent_unregister(&ctrl->death_timer);
! 1239:
! 1240: /* Start timer to call ppp_l2tp_ctrl_do_close() */
! 1241: if (pevent_register(ctrl->ctx, &ctrl->close_timer, 0, ctrl->mutex,
! 1242: ppp_l2tp_ctrl_do_close, ctrl, PEVENT_TIME, 0) == -1)
! 1243: CLOG(LOG_ERR, "error starting close timer: %m");
! 1244: }
! 1245:
! 1246: /*
! 1247: * Close a control connection gracefully.
! 1248: *
! 1249: * We call this in a separate event thread to avoid reentrancy problems.
! 1250: */
! 1251: static void
! 1252: ppp_l2tp_ctrl_do_close(void *arg)
! 1253: {
! 1254: struct ppp_l2tp_ctrl *const ctrl = arg;
! 1255: struct ppp_l2tp_sess *sess;
! 1256: struct ghash_walk walk;
! 1257:
! 1258: /* Remove event */
! 1259: pevent_unregister(&ctrl->close_timer);
! 1260:
! 1261: /* Notify link side about all sessions first */
! 1262: ghash_walk_init(ctrl->sessions, &walk);
! 1263: while ((sess = ghash_walk_next(ctrl->sessions, &walk)) != NULL) {
! 1264: if (sess->link_notified)
! 1265: continue;
! 1266: sess->link_notified = 1;
! 1267: sess->result = L2TP_RESULT_ERROR;
! 1268: sess->error = L2TP_ERROR_GENERIC;
! 1269: FREE(SESS_MEM_TYPE, sess->errmsg);
! 1270: sess->errmsg = STRDUP(SESS_MEM_TYPE,
! 1271: "control connection closing");
! 1272: (*ctrl->cb->terminated)(sess,
! 1273: sess->result, sess->error, sess->errmsg);
! 1274: sess->link_cookie = NULL;
! 1275: }
! 1276:
! 1277: /* Now notify link side about control connection */
! 1278: if (!ctrl->link_notified) {
! 1279: ctrl->link_notified = 1;
! 1280: (*ctrl->cb->ctrl_terminated)(ctrl, ctrl->result, ctrl->error,
! 1281: (ctrl->errmsg != NULL) ? ctrl->errmsg : "");
! 1282: ctrl->link_cookie = NULL;
! 1283: }
! 1284:
! 1285: /* If no active sessions exist, start dying */
! 1286: if (ctrl->active_sessions == 0) {
! 1287: ppp_l2tp_ctrl_death_start(ctrl);
! 1288: return;
! 1289: }
! 1290:
! 1291: /* Close all active sessions */
! 1292: ghash_walk_init(ctrl->sessions, &walk);
! 1293: while ((sess = ghash_walk_next(ctrl->sessions, &walk)) != NULL) {
! 1294: sess->peer_notified = 1; /* no need to notify peer */
! 1295: ppp_l2tp_sess_close(sess, L2TP_RESULT_ERROR,
! 1296: L2TP_ERROR_GENERIC, "control connection closing");
! 1297: }
! 1298: }
! 1299:
! 1300: /*
! 1301: * Notify link side that the control connection has gone away
! 1302: * and begin death timer.
! 1303: *
! 1304: * We rig things so that all the session death notifications happen
! 1305: * before the control connection notification, which happens here.
! 1306: */
! 1307: static void
! 1308: ppp_l2tp_ctrl_death_start(struct ppp_l2tp_ctrl *ctrl)
! 1309: {
! 1310: /* Sanity */
! 1311: assert(ctrl->state == CS_DYING);
! 1312:
! 1313: /* Stop timers */
! 1314: pevent_unregister(&ctrl->idle_timer);
! 1315: pevent_unregister(&ctrl->reply_timer);
! 1316: pevent_unregister(&ctrl->close_timer);
! 1317: pevent_unregister(&ctrl->death_timer);
! 1318:
! 1319: /* Linger for a while before going away */
! 1320: if (pevent_register(ctrl->ctx, &ctrl->death_timer, 0,
! 1321: ctrl->mutex, ppp_l2tp_ctrl_death_timeout, ctrl,
! 1322: PEVENT_TIME, L2TP_CTRL_DEATH_TIMEOUT * 1000) == -1)
! 1323: CLOG(LOG_ERR, "error starting death timer: %m");
! 1324: }
! 1325:
! 1326: /*
! 1327: * Handle idle timeout on control connection.
! 1328: */
! 1329: static void
! 1330: ppp_l2tp_idle_timeout(void *arg)
! 1331: {
! 1332: struct ppp_l2tp_ctrl *const ctrl = arg;
! 1333:
! 1334: /* Remove event */
! 1335: pevent_unregister(&ctrl->idle_timer);
! 1336:
! 1337: /* Restart idle timer */
! 1338: if (pevent_register(ctrl->ctx, &ctrl->idle_timer, 0,
! 1339: ctrl->mutex, ppp_l2tp_idle_timeout, ctrl, PEVENT_TIME,
! 1340: L2TP_IDLE_TIMEOUT * 1000) == -1)
! 1341: CLOG(LOG_ERR, "error restarting idle timer: %m");
! 1342:
! 1343: /* Send a 'hello' packet */
! 1344: ppp_l2tp_ctrl_send(ctrl, 0, HELLO, NULL);
! 1345: }
! 1346:
! 1347: /*
! 1348: * Remove a control connection that has been dead for a while.
! 1349: */
! 1350: static void
! 1351: ppp_l2tp_ctrl_death_timeout(void *arg)
! 1352: {
! 1353: struct ppp_l2tp_ctrl *ctrl = arg;
! 1354:
! 1355: pevent_unregister(&ctrl->death_timer);
! 1356: ppp_l2tp_ctrl_destroy(&ctrl);
! 1357: }
! 1358:
! 1359: /************************************************************************
! 1360: INTERNAL FUNCTIONS: SESSIONS
! 1361: ************************************************************************/
! 1362:
! 1363: /*
! 1364: * This function handles the situation of a locally initiated incoming
! 1365: * call, i.e., we are the LAC. Before sending the ICCN to the LNS, two
! 1366: * events must happen: the LNS must reply with a ICRP, and the link
! 1367: * side must invoke the ppp_l2tp_connected() function.
! 1368: */
! 1369: static int
! 1370: ppp_l2tp_sess_check_liic(struct ppp_l2tp_sess *sess)
! 1371: {
! 1372: struct ppp_l2tp_ctrl *const ctrl = sess->ctrl;
! 1373:
! 1374: /* Are we ready to send ICCN yet? */
! 1375: if (!sess->link_responded || !sess->peer_responded)
! 1376: return (0);
! 1377:
! 1378: /* Set up session */
! 1379: if (ppp_l2tp_sess_setup(sess) == -1)
! 1380: return (-1);
! 1381:
! 1382: /* Now send ICCN to peer */
! 1383: ppp_l2tp_ctrl_send(ctrl, sess->peer_id, ICCN, sess->my_avps);
! 1384:
! 1385: /* Done */
! 1386: return (0);
! 1387: }
! 1388:
! 1389: /*
! 1390: * Set up a session that has reached the established state.
! 1391: */
! 1392: static int
! 1393: ppp_l2tp_sess_setup(struct ppp_l2tp_sess *sess)
! 1394: {
! 1395: union {
! 1396: u_char buf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)];
! 1397: struct ng_mesg reply;
! 1398: } repbuf;
! 1399: struct ng_mesg *const reply = &repbuf.reply;
! 1400: struct nodeinfo ninfo;
! 1401: struct ppp_l2tp_ctrl *const ctrl = sess->ctrl;
! 1402: struct ngm_mkpeer mkpeer;
! 1403: char path[64];
! 1404:
! 1405: /* If link side is waiting for confirmation, schedule it */
! 1406: if (sess->side == SIDE_LNS) {
! 1407: pevent_unregister(&sess->notify_timer);
! 1408: if (pevent_register(ctrl->ctx, &sess->notify_timer, 0,
! 1409: ctrl->mutex, ppp_l2tp_sess_notify, sess, PEVENT_TIME,
! 1410: 0) == -1) {
! 1411: CLOG(LOG_ERR, "error starting notify timer: %m");
! 1412: goto fail;
! 1413: }
! 1414: }
! 1415:
! 1416: /* Attach a 'tee' node so l2tp node never sees hook disconnect */
! 1417: memset(&mkpeer, 0, sizeof(mkpeer));
! 1418: strlcpy(mkpeer.type, NG_TEE_NODE_TYPE, sizeof(mkpeer.type));
! 1419: strlcpy(mkpeer.ourhook, sess->hook, sizeof(mkpeer.ourhook));
! 1420: strlcpy(mkpeer.peerhook, NG_TEE_HOOK_LEFT, sizeof(mkpeer.peerhook));
! 1421: if (NgSendMsg(ctrl->csock, NG_L2TP_HOOK_CTRL, NGM_GENERIC_COOKIE,
! 1422: NGM_MKPEER, &mkpeer, sizeof(mkpeer)) == -1) {
! 1423: CLOG(LOG_ERR, "%s: %m", "mkpeer");
! 1424: goto fail;
! 1425: }
! 1426:
! 1427: /* Get ng_tee node ID */
! 1428: snprintf(path, sizeof(path), "%s.%s", NG_L2TP_HOOK_CTRL, sess->hook);
! 1429: if (NgSendMsg(ctrl->csock, path,
! 1430: NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) == -1)
! 1431: goto fail;
! 1432: if (NgRecvMsg(ctrl->csock, reply, sizeof(repbuf), NULL) == -1)
! 1433: goto fail;
! 1434: memcpy(&ninfo, reply->data, sizeof(ninfo));
! 1435: sess->node_id = ninfo.id;
! 1436:
! 1437: /* Configure session hook */
! 1438: sess->config.control_dseq = sess->dseq_required;
! 1439: sess->config.enable_dseq = 1;
! 1440: sess->config.peer_id = sess->peer_id;
! 1441: if (NgSendMsg(ctrl->csock, NG_L2TP_HOOK_CTRL, NGM_L2TP_COOKIE,
! 1442: NGM_L2TP_SET_SESS_CONFIG, &sess->config,
! 1443: sizeof(sess->config)) == -1) {
! 1444: CLOG(LOG_ERR, "error configuring session hook: %m");
! 1445: goto fail;
! 1446: }
! 1447:
! 1448: /* Call is now established */
! 1449: sess->state = SS_ESTABLISHED;
! 1450: return (0);
! 1451:
! 1452: fail:
! 1453: /* Clean up after failure */
! 1454: pevent_unregister(&sess->notify_timer);
! 1455: return (-1);
! 1456: }
! 1457:
! 1458: /*
! 1459: * Close a session gracefully, after the next context switch.
! 1460: */
! 1461: static void
! 1462: ppp_l2tp_sess_close(struct ppp_l2tp_sess *sess,
! 1463: u_int16_t result, u_int16_t error, const char *errmsg)
! 1464: {
! 1465: struct ppp_l2tp_ctrl *const ctrl = sess->ctrl;
! 1466:
! 1467: /* Sanity check */
! 1468: if (sess->state == SS_DYING)
! 1469: return;
! 1470: sess->state = SS_DYING;
! 1471: ctrl->active_sessions--;
! 1472:
! 1473: /* Save result code and error string */
! 1474: sess->result = result;
! 1475: sess->error = error;
! 1476: FREE(SESS_MEM_TYPE, sess->errmsg);
! 1477: sess->errmsg = (errmsg == NULL) ? NULL : STRDUP(SESS_MEM_TYPE, errmsg);
! 1478:
! 1479: /* Notify peer if necessary */
! 1480: if (!sess->peer_notified) {
! 1481: struct ppp_l2tp_avp_list *avps = NULL;
! 1482: const size_t elen = (sess->errmsg == NULL) ?
! 1483: 0 : strlen(sess->errmsg);
! 1484: u_char *rbuf = NULL;
! 1485: u_int16_t value16;
! 1486:
! 1487: /* Create AVP list */
! 1488: sess->peer_notified = 1;
! 1489: if ((avps = ppp_l2tp_avp_list_create()) == NULL) {
! 1490: CLOG(LOG_ERR, "%s: %m", "ppp_l2tp_avp_list_create");
! 1491: goto notify_done;
! 1492: }
! 1493:
! 1494: /* Add assigned session ID AVP */
! 1495: value16 = htons(sess->config.session_id);
! 1496: if (ppp_l2tp_avp_list_append(avps, 1, 0,
! 1497: AVP_ASSIGNED_TUNNEL_ID, &value16, sizeof(value16)) == -1) {
! 1498: CLOG(LOG_ERR, "%s: %m", "ppp_l2tp_avp_list_append");
! 1499: goto notify_done;
! 1500: }
! 1501:
! 1502: /* Add result code AVP */
! 1503: if ((rbuf = MALLOC(TYPED_MEM_TEMP, 4 + elen)) == NULL)
! 1504: goto notify_done;
! 1505: value16 = htons(sess->result);
! 1506: memcpy(rbuf, &value16, sizeof(value16));
! 1507: value16 = htons(sess->error);
! 1508: memcpy(rbuf + 2, &value16, sizeof(value16));
! 1509: memcpy(rbuf + 4, sess->errmsg, elen);
! 1510: if (ppp_l2tp_avp_list_append(avps, 1, 0, AVP_RESULT_CODE,
! 1511: rbuf, 4 + elen) == -1) {
! 1512: CLOG(LOG_ERR, "%s: %m", "ppp_l2tp_avp_list_append");
! 1513: goto notify_done;
! 1514: }
! 1515:
! 1516: /* Send CDN */
! 1517: ppp_l2tp_ctrl_send(ctrl, sess->peer_id, CDN, avps);
! 1518:
! 1519: notify_done:
! 1520: /* Clean up */
! 1521: ppp_l2tp_avp_list_destroy(&avps);
! 1522: FREE(TYPED_MEM_TEMP, rbuf);
! 1523: }
! 1524:
! 1525: /* Stop all session timers */
! 1526: pevent_unregister(&sess->notify_timer);
! 1527: pevent_unregister(&sess->reply_timer);
! 1528: pevent_unregister(&sess->death_timer);
! 1529: pevent_unregister(&sess->close_timer);
! 1530:
! 1531: /* Start timer to call ppp_l2tp_sess_do_close() */
! 1532: if (pevent_register(ctrl->ctx, &sess->close_timer, 0,
! 1533: ctrl->mutex, ppp_l2tp_sess_do_close, sess, PEVENT_TIME, 0) == -1)
! 1534: SLOG(LOG_ERR, "error starting close timer: %m");
! 1535: }
! 1536:
! 1537: /*
! 1538: * Close a session gracefully.
! 1539: *
! 1540: * We call this in a separate event thread to avoid reentrancy problems.
! 1541: */
! 1542: static void
! 1543: ppp_l2tp_sess_do_close(void *arg)
! 1544: {
! 1545: struct ppp_l2tp_sess *const sess = arg;
! 1546: struct ppp_l2tp_ctrl *const ctrl = sess->ctrl;
! 1547:
! 1548: /* Remove event */
! 1549: pevent_unregister(&sess->close_timer);
! 1550:
! 1551: /* Linger for a while before going away */
! 1552: if (pevent_register(ctrl->ctx, &sess->death_timer, 0,
! 1553: ctrl->mutex, ppp_l2tp_sess_death_timeout, sess, PEVENT_TIME,
! 1554: L2TP_SESS_DEATH_TIMEOUT * 1000) == -1)
! 1555: CLOG(LOG_ERR, "error starting death timer: %m");
! 1556:
! 1557: /* Notify link side about session if necessary */
! 1558: if (!sess->link_notified) {
! 1559: sess->link_notified = 1;
! 1560: (*ctrl->cb->terminated)(sess, sess->result, sess->error,
! 1561: (sess->errmsg != NULL) ? sess->errmsg : "");
! 1562: sess->link_cookie = NULL;
! 1563: }
! 1564:
! 1565: /* Close control connection after last session closes */
! 1566: if (ctrl->active_sessions == 0) {
! 1567: if (ctrl->state != CS_DYING) {
! 1568: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_CLEARED,
! 1569: 0, "no more sessions exist in this tunnel");
! 1570: } else
! 1571: ppp_l2tp_ctrl_death_start(ctrl);
! 1572: }
! 1573: }
! 1574:
! 1575: /*
! 1576: * Notify link side that a session is connected.
! 1577: *
! 1578: * We call this in a separate event thread to avoid reentrancy problems.
! 1579: */
! 1580: static void
! 1581: ppp_l2tp_sess_notify(void *arg)
! 1582: {
! 1583: struct ppp_l2tp_sess *const sess = arg;
! 1584: struct ppp_l2tp_ctrl *const ctrl = sess->ctrl;
! 1585:
! 1586: pevent_unregister(&sess->notify_timer);
! 1587: (*ctrl->cb->connected)(sess, sess->peer_avps);
! 1588: }
! 1589:
! 1590: /*
! 1591: * Remove a session that has been dead for a while.
! 1592: */
! 1593: static void
! 1594: ppp_l2tp_sess_death_timeout(void *arg)
! 1595: {
! 1596: struct ppp_l2tp_sess *sess = arg;
! 1597:
! 1598: pevent_unregister(&sess->death_timer);
! 1599: ppp_l2tp_sess_destroy(&sess);
! 1600: }
! 1601:
! 1602: /************************************************************************
! 1603: NETGRAPH SOCKET READERS
! 1604: ************************************************************************/
! 1605:
! 1606: /*
! 1607: * Read from netgraph data socket. This is where incoming L2TP
! 1608: * control connection messages appear.
! 1609: */
! 1610: static void
! 1611: ppp_l2tp_data_event(void *arg)
! 1612: {
! 1613: struct ppp_l2tp_ctrl *const ctrl = arg;
! 1614: const struct l2tp_msg_info *msg_info;
! 1615: struct ppp_l2tp_avp_list *avps = NULL;
! 1616: struct ppp_l2tp_avp_ptrs *ptrs = NULL;
! 1617: struct ppp_l2tp_sess *sess;
! 1618: struct ppp_l2tp_sess key;
! 1619: static u_char buf[8192];
! 1620: u_int16_t msgtype;
! 1621: char ebuf[64];
! 1622: int len;
! 1623: int i;
! 1624: int j;
! 1625:
! 1626: /* Restart idle timer */
! 1627: pevent_unregister(&ctrl->idle_timer);
! 1628: if (pevent_register(ctrl->ctx, &ctrl->idle_timer, 0,
! 1629: ctrl->mutex, ppp_l2tp_idle_timeout, ctrl, PEVENT_TIME,
! 1630: L2TP_IDLE_TIMEOUT * 1000) == -1) {
! 1631: CLOG(LOG_ERR, "error restarting idle timer: %m");
! 1632: goto fail_errno;
! 1633: }
! 1634:
! 1635: /* Read packet */
! 1636: if ((len = read(ctrl->dsock, buf, sizeof(buf))) == -1) {
! 1637: CLOG(LOG_ERR, "error reading ctrl hook: %m");
! 1638: goto fail_errno;
! 1639: }
! 1640:
! 1641: /* Extract session ID */
! 1642: memcpy(&key.config.session_id, buf, 2);
! 1643: key.config.session_id = ntohs(key.config.session_id);
! 1644:
! 1645: /* Parse out AVP's */
! 1646: if ((avps = ppp_l2tp_avp_unpack(ppp_l2tp_avp_info_list,
! 1647: buf + 2, len - 2, ctrl->secret, ctrl->seclen)) == NULL) {
! 1648: switch (errno) {
! 1649: case EILSEQ:
! 1650: CLOG(LOG_WARNING,
! 1651: "rec'd improperly formatted control message");
! 1652: goto fail_invalid;
! 1653: case EAUTH:
! 1654: CLOG(LOG_WARNING,
! 1655: "rec'd hidden AVP, but no shared secret");
! 1656: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_ERROR,
! 1657: L2TP_ERROR_GENERIC, "hidden AVP found"
! 1658: " but no shared secret configured");
! 1659: goto done;
! 1660: case ENOSYS:
! 1661: CLOG(LOG_WARNING, "rec'd mandatory but unknown AVP");
! 1662: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_ERROR,
! 1663: L2TP_ERROR_MANDATORY, NULL);
! 1664: goto done;
! 1665: default:
! 1666: CLOG(LOG_ERR, "error decoding control message: %m");
! 1667: goto fail_errno;
! 1668: }
! 1669: }
! 1670:
! 1671: /* Debugging */
! 1672: if (key.config.session_id == 0)
! 1673: ppp_l2tp_ctrl_dump(ctrl, avps, "RECV: ");
! 1674: else {
! 1675: ppp_l2tp_ctrl_dump(ctrl, avps, "RECV(0x%04x): ",
! 1676: ntohs(key.config.session_id));
! 1677: }
! 1678:
! 1679: /* Message type AVP must be present and first */
! 1680: if (avps->length == 0 || avps->avps[0].type != AVP_MESSAGE_TYPE) {
! 1681: CLOG(LOG_WARNING,
! 1682: "rec'd ctrl message lacking message type AVP");
! 1683: goto fail_invalid;
! 1684: }
! 1685:
! 1686: /* Get message type from message type AVP */
! 1687: memcpy(&msgtype, avps->avps[0].value, 2);
! 1688: msgtype = ntohs(msgtype);
! 1689:
! 1690: /* Find descriptor for this message type */
! 1691: for (i = 0; ppp_l2tp_msg_info[i].name != NULL
! 1692: && msgtype != ppp_l2tp_msg_info[i].type; i++);
! 1693: if ((msg_info = &ppp_l2tp_msg_info[i])->name == NULL) {
! 1694: if (avps->avps[0].mandatory) {
! 1695: snprintf(ebuf, sizeof(ebuf), "rec'd unsupported"
! 1696: " but mandatory message type %u", msgtype);
! 1697: CLOG(LOG_WARNING, "%s", ebuf);
! 1698: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_ERROR,
! 1699: L2TP_ERROR_BAD_VALUE, ebuf);
! 1700: goto done;
! 1701: }
! 1702: CLOG(LOG_NOTICE,
! 1703: "rec'd unknown message type %u; ignoring", msgtype);
! 1704: goto done; /* just ignore it */
! 1705: }
! 1706:
! 1707: /* Check for missing required AVP's */
! 1708: for (i = 0; msg_info->req_avps[i] != -1; i++) {
! 1709: for (j = 0; j < avps->length
! 1710: && avps->avps[j].type != msg_info->req_avps[i]; j++);
! 1711: if (j == avps->length) {
! 1712: snprintf(ebuf, sizeof(ebuf), "rec'd %s control"
! 1713: " message lacking required AVP #%u",
! 1714: msg_info->name, msg_info->req_avps[i]);
! 1715: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_ERROR,
! 1716: L2TP_ERROR_BAD_VALUE, ebuf);
! 1717: goto done;
! 1718: }
! 1719: }
! 1720:
! 1721: /* Convert AVP's to friendly form */
! 1722: if ((ptrs = ppp_l2tp_avp_list2ptrs(avps)) == NULL) {
! 1723: CLOG(LOG_ERR, "error decoding AVP list: %m");
! 1724: goto fail_errno;
! 1725: }
! 1726:
! 1727: /* If this is a tunnel-level command, do it */
! 1728: if (msg_info->ctrl_handler != NULL) {
! 1729:
! 1730: /* Check for valid control connection state */
! 1731: for (i = 0; msg_info->valid_states[i] != -1
! 1732: && msg_info->valid_states[i] != ctrl->state; i++);
! 1733: if (msg_info->valid_states[i] == -1) {
! 1734:
! 1735: /* Could be in CS_DYING if we just closed the tunnel */
! 1736: if (ctrl->state == CS_DYING) {
! 1737: snprintf(ebuf, sizeof(ebuf),
! 1738: "ignoring %s in state %s", msg_info->name,
! 1739: ppp_l2tp_ctrl_state_str(ctrl->state));
! 1740: CLOG(LOG_INFO, "%s", ebuf);
! 1741: goto done;
! 1742: }
! 1743:
! 1744: /* Log a warning message because the peer is broken */
! 1745: snprintf(ebuf, sizeof(ebuf),
! 1746: "rec'd %s in state %s", msg_info->name,
! 1747: ppp_l2tp_ctrl_state_str(ctrl->state));
! 1748: CLOG(LOG_WARNING, "%s", ebuf);
! 1749: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_FSM, 0, ebuf);
! 1750: goto done;
! 1751: }
! 1752:
! 1753: /* Cancel reply timer and invoke handler */
! 1754: CLOG((msgtype == HELLO) ? LOG_DEBUG : LOG_INFO,
! 1755: "rec'd %s in state %s", msg_info->name,
! 1756: ppp_l2tp_ctrl_state_str(ctrl->state));
! 1757: pevent_unregister(&ctrl->reply_timer);
! 1758: if ((*msg_info->ctrl_handler)(ctrl, avps, ptrs) == -1)
! 1759: goto fail_errno;
! 1760:
! 1761: /* If we're now expecting a reply, start expecting it */
! 1762: ppp_l2tp_ctrl_check_reply(ctrl);
! 1763: goto done;
! 1764: }
! 1765:
! 1766: /* Find associated session */
! 1767: if (key.config.session_id == 0) {
! 1768: struct ghash_walk walk;
! 1769:
! 1770: /* This should only happen with CDN messages */
! 1771: if (msgtype != CDN) {
! 1772: CLOG(LOG_NOTICE, "rec'd %s with zero session ID",
! 1773: msg_info->name);
! 1774: goto done;
! 1775: }
! 1776:
! 1777: /* Find session with 'reverse lookup' using peer's session ID */
! 1778: ghash_walk_init(ctrl->sessions, &walk);
! 1779: while ((sess = ghash_walk_next(ctrl->sessions, &walk)) != NULL
! 1780: && sess->peer_id != key.config.session_id);
! 1781: if (sess == NULL)
! 1782: goto done;
! 1783: } else if ((sess = ghash_get(ctrl->sessions, &key)) == NULL) {
! 1784: CLOG(LOG_NOTICE, "rec'd %s for unknown session 0x%04x",
! 1785: msg_info->name, key.config.session_id);
! 1786: goto done;
! 1787: }
! 1788:
! 1789: /* Check for valid session state, origination, and side */
! 1790: for (i = 0; msg_info->valid_states[i] != -1
! 1791: && msg_info->valid_states[i] != sess->state; i++);
! 1792: if (msg_info->valid_states[i] == -1) {
! 1793: snprintf(ebuf, sizeof(ebuf), "rec'd %s in state %s",
! 1794: msg_info->name, ppp_l2tp_sess_state_str(sess->state));
! 1795: SLOG(LOG_WARNING, "%s", ebuf);
! 1796: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_FSM, 0, ebuf);
! 1797: goto done;
! 1798: }
! 1799: for (i = 0; msg_info->valid_orig[i] != -1
! 1800: && msg_info->valid_orig[i] != sess->orig; i++);
! 1801: if (msg_info->valid_orig[i] == -1) {
! 1802: snprintf(ebuf, sizeof(ebuf), "rec'd %s in state %s,"
! 1803: " but session originated %sly", msg_info->name,
! 1804: ppp_l2tp_sess_state_str(sess->state),
! 1805: ppp_l2tp_sess_orig_str(sess->orig));
! 1806: SLOG(LOG_WARNING, "%s", ebuf);
! 1807: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_FSM, 0, ebuf);
! 1808: goto done;
! 1809: }
! 1810: for (i = 0; msg_info->valid_side[i] != -1
! 1811: && msg_info->valid_side[i] != sess->side; i++);
! 1812: if (msg_info->valid_side[i] == -1) {
! 1813: snprintf(ebuf, sizeof(ebuf), "rec'd %s in state %s,"
! 1814: " but we are %s for this session", msg_info->name,
! 1815: ppp_l2tp_sess_state_str(sess->state),
! 1816: ppp_l2tp_sess_side_str(sess->side));
! 1817: SLOG(LOG_WARNING, "%s", ebuf);
! 1818: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_FSM, 0, ebuf);
! 1819: goto done;
! 1820: }
! 1821:
! 1822: /* Cancel reply timer and invoke handler */
! 1823: SLOG(LOG_INFO, "rec'd %s in state %s",
! 1824: msg_info->name, ppp_l2tp_sess_state_str(sess->state));
! 1825: pevent_unregister(&sess->reply_timer);
! 1826: if ((*msg_info->sess_handler)(sess, avps, ptrs) == -1)
! 1827: goto fail_errno;
! 1828:
! 1829: /* If we're now expecting a reply, start expecting it */
! 1830: ppp_l2tp_sess_check_reply(sess);
! 1831: goto done;
! 1832:
! 1833: fail_invalid:
! 1834: /* Fail because of a bogus message */
! 1835: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_ERROR,
! 1836: L2TP_ERROR_BAD_VALUE, "improperly formatted control message");
! 1837: goto done;
! 1838:
! 1839: fail_errno:
! 1840: /* Fail because of a system error */
! 1841: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_ERROR,
! 1842: L2TP_ERROR_GENERIC, strerror(errno));
! 1843:
! 1844: done:
! 1845: /* Clean up */
! 1846: ppp_l2tp_avp_list_destroy(&avps);
! 1847: ppp_l2tp_avp_ptrs_destroy(&ptrs);
! 1848: }
! 1849:
! 1850: /*
! 1851: * Read from netgraph control socket. This is where incoming
! 1852: * netgraph control messages appear.
! 1853: */
! 1854: static void
! 1855: ppp_l2tp_ctrl_event(void *arg)
! 1856: {
! 1857: struct ppp_l2tp_ctrl *const ctrl = arg;
! 1858: union {
! 1859: u_char buf[128];
! 1860: struct ng_mesg msg;
! 1861: } buf;
! 1862: struct ng_mesg *const msg = &buf.msg;
! 1863: char raddr[NG_PATHSIZ];
! 1864: int len;
! 1865:
! 1866: /* Read netgraph control message */
! 1867: if ((len = NgRecvMsg(ctrl->csock, msg, sizeof(buf), raddr)) < 0) {
! 1868: CLOG(LOG_ERR, "error reading control message: %m");
! 1869: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_ERROR,
! 1870: L2TP_ERROR_GENERIC, strerror(errno));
! 1871: return;
! 1872: }
! 1873:
! 1874: /* Examine message */
! 1875: switch (msg->header.typecookie) {
! 1876: case NGM_L2TP_COOKIE:
! 1877: switch (msg->header.cmd) {
! 1878: case NGM_L2TP_ACK_FAILURE:
! 1879: if (ctrl->state != CS_DYING) {
! 1880: CLOG(LOG_WARNING,
! 1881: "L2TP acknowledgement timeout");
! 1882: ppp_l2tp_ctrl_close(ctrl,
! 1883: L2TP_RESULT_CLEARED, 0, NULL);
! 1884: }
! 1885: break;
! 1886: default:
! 1887: break;
! 1888: }
! 1889: break;
! 1890: default:
! 1891: break;
! 1892: }
! 1893: }
! 1894:
! 1895: /************************************************************************
! 1896: INCOMING CONTROL MESSAGE HANDLERS
! 1897: ************************************************************************/
! 1898:
! 1899: static int
! 1900: ppp_l2tp_handle_SCCRQ(struct ppp_l2tp_ctrl *ctrl,
! 1901: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 1902: {
! 1903: struct ppp_l2tp_ctrl *ctrl2;
! 1904: const u_char *tiebreaker;
! 1905: struct ghash_walk walk;
! 1906: int diff;
! 1907: int i;
! 1908:
! 1909: /* See if there is an outstanding SCCRQ to this peer */
! 1910: ghash_walk_init(ppp_l2tp_ctrls, &walk);
! 1911: while ((ctrl2 = ghash_walk_next(ppp_l2tp_ctrls, &walk)) != NULL) {
! 1912: if (ctrl2 != ctrl
! 1913: && ctrl2->peer_id == ctrl->peer_id
! 1914: && ctrl2->state == CS_WAIT_CTL_REPLY)
! 1915: break;
! 1916: }
! 1917: if (ctrl2 == NULL)
! 1918: goto ok;
! 1919:
! 1920: /* Determine if we used a tie-breaker in our SCCRQ */
! 1921: for (tiebreaker = NULL, i = 0; i < ctrl2->avps->length; i++) {
! 1922: struct ppp_l2tp_avp *const avp = &ctrl2->avps->avps[i];
! 1923:
! 1924: if (avp->vendor == 0 && avp->type == AVP_TIE_BREAKER) {
! 1925: tiebreaker = avp->value;
! 1926: break;
! 1927: }
! 1928: }
! 1929:
! 1930: /* If neither side used a tie-breaker, allow this connection */
! 1931: if (tiebreaker == NULL && ptrs->tiebreaker == NULL)
! 1932: goto ok;
! 1933:
! 1934: /* Compare tie-breaker values to see who wins */
! 1935: if (tiebreaker == NULL) /* peer wins */
! 1936: diff = 1;
! 1937: else if (ptrs->tiebreaker == NULL) /* i win */
! 1938: diff = -1;
! 1939: else /* compare values */
! 1940: diff = memcmp(tiebreaker, &ptrs->tiebreaker->value, 8);
! 1941: if (diff == 0) { /* we both lose */
! 1942: CLOG(LOG_NOTICE, "SCCRQ tie: we both lose");
! 1943: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_DUP_CTRL, 0, NULL);
! 1944: ppp_l2tp_ctrl_close(ctrl2, L2TP_RESULT_DUP_CTRL, 0, NULL);
! 1945: return (0);
! 1946: }
! 1947: if (diff > 0) { /* i win */
! 1948: CLOG(LOG_NOTICE, "SCCRQ tie: peer loses");
! 1949: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_DUP_CTRL, 0, NULL);
! 1950: return (0);
! 1951: }
! 1952: CLOG(LOG_NOTICE, "SCCRQ tie: peer wins");
! 1953: ppp_l2tp_ctrl_close(ctrl2, L2TP_RESULT_DUP_CTRL, 0, NULL);
! 1954:
! 1955: ok:
! 1956: /* Do control connection setup */
! 1957: if (ppp_l2tp_ctrl_setup_1(ctrl, ptrs) == -1)
! 1958: return (-1);
! 1959:
! 1960: /* Send response and update state */
! 1961: ppp_l2tp_ctrl_send(ctrl, 0, SCCRP, ctrl->avps);
! 1962: ctrl->state = CS_WAIT_CTL_CONNECT;
! 1963: return (0);
! 1964: }
! 1965:
! 1966: static int
! 1967: ppp_l2tp_handle_SCCRP(struct ppp_l2tp_ctrl *ctrl,
! 1968: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 1969: {
! 1970: /* Do control connection setup */
! 1971: if (ppp_l2tp_ctrl_setup_1(ctrl, ptrs) == -1)
! 1972: return (-1);
! 1973: if (ppp_l2tp_ctrl_setup_2(ctrl, ptrs) == -1)
! 1974: return (-1);
! 1975:
! 1976: /* Send response and update state */
! 1977: ppp_l2tp_ctrl_send(ctrl, 0, SCCCN, ctrl->avps);
! 1978: ctrl->state = CS_WAIT_CTL_CONNECT;
! 1979: return (0);
! 1980: }
! 1981:
! 1982: static int
! 1983: ppp_l2tp_handle_SCCCN(struct ppp_l2tp_ctrl *ctrl,
! 1984: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 1985: {
! 1986: /* Do control connection setup */
! 1987: if (ppp_l2tp_ctrl_setup_2(ctrl, ptrs) == -1)
! 1988: return (-1);
! 1989:
! 1990: /* Update state */
! 1991: ctrl->state = CS_ESTABLISHED;
! 1992: return (0);
! 1993: }
! 1994:
! 1995: static int
! 1996: ppp_l2tp_handle_StopCCN(struct ppp_l2tp_ctrl *ctrl,
! 1997: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 1998: {
! 1999: struct ppp_l2tp_sess *sess;
! 2000: struct ghash_walk walk;
! 2001:
! 2002: /* StopCCN implies closing all sessions */
! 2003: ctrl->peer_notified = 1;
! 2004: ghash_walk_init(ctrl->sessions, &walk);
! 2005: while ((sess = ghash_walk_next(ctrl->sessions, &walk)) != NULL)
! 2006: sess->peer_notified = 1;
! 2007:
! 2008: /* Close control connection */
! 2009: ppp_l2tp_ctrl_close(ctrl, ptrs->errresultcode->result,
! 2010: ptrs->errresultcode->error, ptrs->errresultcode->errmsg);
! 2011: return (0);
! 2012: }
! 2013:
! 2014: static int
! 2015: ppp_l2tp_handle_HELLO(struct ppp_l2tp_ctrl *ctrl,
! 2016: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 2017: {
! 2018: return (0);
! 2019: }
! 2020:
! 2021: static int
! 2022: ppp_l2tp_handle_OCRQ(struct ppp_l2tp_ctrl *ctrl,
! 2023: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 2024: {
! 2025: struct ppp_l2tp_sess *sess;
! 2026:
! 2027: /* Create new session */
! 2028: if ((sess = ppp_l2tp_sess_create(ctrl, ORIG_REMOTE, SIDE_LAC)) == NULL)
! 2029: return (-1);
! 2030: sess->peer_id = ptrs->sessionid->id;
! 2031:
! 2032: /* Send response */
! 2033: ppp_l2tp_ctrl_send(ctrl, sess->peer_id, OCRP, sess->my_avps);
! 2034:
! 2035: /* Notify link side */
! 2036: (*ctrl->cb->initiated)(ctrl, sess, 1, avps);
! 2037:
! 2038: /* Clean up */
! 2039: return (0);
! 2040: }
! 2041:
! 2042: static int
! 2043: ppp_l2tp_handle_ICRQ(struct ppp_l2tp_ctrl *ctrl,
! 2044: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 2045: {
! 2046: struct ppp_l2tp_sess *sess;
! 2047:
! 2048: /* Create new session */
! 2049: if ((sess = ppp_l2tp_sess_create(ctrl, ORIG_REMOTE, SIDE_LNS)) == NULL)
! 2050: return (-1);
! 2051: sess->peer_id = ptrs->sessionid->id;
! 2052:
! 2053: /* Send response */
! 2054: ppp_l2tp_ctrl_send(ctrl, sess->peer_id, ICRP, sess->my_avps);
! 2055: ppp_l2tp_sess_check_reply(sess);
! 2056:
! 2057: /* Notify link side */
! 2058: (*ctrl->cb->initiated)(ctrl, sess, 0, avps);
! 2059:
! 2060: /* Clean up */
! 2061: return (0);
! 2062: }
! 2063:
! 2064: static int
! 2065: ppp_l2tp_handle_OCRP(struct ppp_l2tp_sess *sess,
! 2066: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 2067: {
! 2068: sess->peer_id = ptrs->sessionid->id;
! 2069: sess->state = SS_WAIT_CONNECT;
! 2070: return (0);
! 2071: }
! 2072:
! 2073: static int
! 2074: ppp_l2tp_handle_xCCN(struct ppp_l2tp_sess *sess,
! 2075: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 2076: {
! 2077: /* Save peer's AVP's for this session */
! 2078: ppp_l2tp_avp_list_destroy(&sess->peer_avps);
! 2079: if ((sess->peer_avps = ppp_l2tp_avp_list_copy(avps)) == NULL)
! 2080: return (-1);
! 2081:
! 2082: /* Set up session */
! 2083: sess->dseq_required = (ptrs->seqrequired != NULL);
! 2084: if (ppp_l2tp_sess_setup(sess) == -1)
! 2085: return (-1);
! 2086:
! 2087: /* Done */
! 2088: return (0);
! 2089: }
! 2090:
! 2091: static int
! 2092: ppp_l2tp_handle_ICRP(struct ppp_l2tp_sess *sess,
! 2093: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 2094: {
! 2095: struct ppp_l2tp_ctrl *const ctrl = sess->ctrl;
! 2096: char buf[64];
! 2097:
! 2098: /* Save peer ID */
! 2099: sess->peer_id = ptrs->sessionid->id;
! 2100:
! 2101: /* Detect duplicate ICRP's */
! 2102: if (sess->peer_responded) {
! 2103: snprintf(buf, sizeof(buf), "rec'd duplicate %s in state %s",
! 2104: "ICRP", ppp_l2tp_sess_state_str(sess->state));
! 2105: SLOG(LOG_WARNING, "%s", buf);
! 2106: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_FSM, 0, buf);
! 2107: return (0);
! 2108: }
! 2109:
! 2110: /* Check status for locally initiated incoming call */
! 2111: sess->peer_responded = 1;
! 2112: if (ppp_l2tp_sess_check_liic(sess) == -1)
! 2113: return (-1);
! 2114:
! 2115: /* Done */
! 2116: return (0);
! 2117: }
! 2118:
! 2119: static int
! 2120: ppp_l2tp_handle_CDN(struct ppp_l2tp_sess *sess,
! 2121: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 2122: {
! 2123: sess->peer_notified = 1;
! 2124: ppp_l2tp_sess_close(sess, ptrs->errresultcode->result,
! 2125: ptrs->errresultcode->error, ptrs->errresultcode->errmsg);
! 2126: return (0);
! 2127: }
! 2128:
! 2129: static int
! 2130: ppp_l2tp_handle_SLI(struct ppp_l2tp_sess *sess,
! 2131: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 2132: {
! 2133: struct ppp_l2tp_ctrl *const ctrl = sess->ctrl;
! 2134:
! 2135: if (ctrl->cb->set_link_info == NULL)
! 2136: return (0);
! 2137: (*ctrl->cb->set_link_info)(sess, ptrs->accm->xmit, ptrs->accm->xmit);
! 2138: return (0);
! 2139: }
! 2140:
! 2141: static int
! 2142: ppp_l2tp_handle_WEN(struct ppp_l2tp_sess *sess,
! 2143: const struct ppp_l2tp_avp_list *avps, struct ppp_l2tp_avp_ptrs *ptrs)
! 2144: {
! 2145: struct ppp_l2tp_ctrl *const ctrl = sess->ctrl;
! 2146:
! 2147: if (ctrl->cb->wan_error_notify == NULL)
! 2148: return (0);
! 2149: (*ctrl->cb->wan_error_notify)(sess, ptrs->callerror->crc,
! 2150: ptrs->callerror->frame, ptrs->callerror->overrun,
! 2151: ptrs->callerror->buffer, ptrs->callerror->timeout,
! 2152: ptrs->callerror->alignment);
! 2153: return (0);
! 2154: }
! 2155:
! 2156: /************************************************************************
! 2157: REPLY EXPECTORS
! 2158: ************************************************************************/
! 2159:
! 2160: static pevent_handler_t ppp_l2tp_ctrl_reply_timeout;
! 2161: static pevent_handler_t ppp_l2tp_sess_reply_timeout;
! 2162:
! 2163: static void
! 2164: ppp_l2tp_ctrl_check_reply(struct ppp_l2tp_ctrl *ctrl)
! 2165: {
! 2166: pevent_unregister(&ctrl->reply_timer);
! 2167: switch (ctrl->state) {
! 2168: case CS_IDLE:
! 2169: case CS_WAIT_CTL_REPLY:
! 2170: case CS_WAIT_CTL_CONNECT:
! 2171: if (pevent_register(ctrl->ctx, &ctrl->reply_timer, 0,
! 2172: ctrl->mutex, ppp_l2tp_ctrl_reply_timeout, ctrl, PEVENT_TIME,
! 2173: L2TP_REPLY_TIMEOUT * 1000) == -1)
! 2174: CLOG(LOG_ERR, "error starting reply timer: %m");
! 2175: break;
! 2176: default:
! 2177: break;
! 2178: }
! 2179: }
! 2180:
! 2181: static void
! 2182: ppp_l2tp_sess_check_reply(struct ppp_l2tp_sess *sess)
! 2183: {
! 2184: struct ppp_l2tp_ctrl *const ctrl = sess->ctrl;
! 2185:
! 2186: pevent_unregister(&sess->reply_timer);
! 2187: switch (sess->state) {
! 2188: case SS_WAIT_REPLY:
! 2189: case SS_WAIT_CONNECT:
! 2190: if (pevent_register(ctrl->ctx, &sess->reply_timer, 0,
! 2191: ctrl->mutex, ppp_l2tp_sess_reply_timeout, sess, PEVENT_TIME,
! 2192: L2TP_REPLY_TIMEOUT * 1000) == -1)
! 2193: SLOG(LOG_ERR, "error starting reply timer: %m");
! 2194: break;
! 2195: default:
! 2196: break;
! 2197: }
! 2198: }
! 2199:
! 2200: static void
! 2201: ppp_l2tp_ctrl_reply_timeout(void *arg)
! 2202: {
! 2203: struct ppp_l2tp_ctrl *const ctrl = arg;
! 2204:
! 2205: pevent_unregister(&ctrl->reply_timer);
! 2206: CLOG(LOG_NOTICE, "reply timeout in state %s",
! 2207: ppp_l2tp_ctrl_state_str(ctrl->state));
! 2208: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_ERROR,
! 2209: L2TP_ERROR_GENERIC, "expecting reply; none received");
! 2210: }
! 2211:
! 2212: static void
! 2213: ppp_l2tp_sess_reply_timeout(void *arg)
! 2214: {
! 2215: struct ppp_l2tp_sess *const sess = arg;
! 2216: struct ppp_l2tp_ctrl *const ctrl = sess->ctrl;
! 2217:
! 2218: pevent_unregister(&sess->reply_timer);
! 2219: SLOG(LOG_NOTICE, "reply timeout in state %s",
! 2220: ppp_l2tp_sess_state_str(sess->state));
! 2221: ppp_l2tp_ctrl_close(ctrl, L2TP_RESULT_ERROR,
! 2222: L2TP_ERROR_GENERIC, "expecting reply; none received");
! 2223: }
! 2224:
! 2225: /************************************************************************
! 2226: CONTROL AND SESSION OBJECT DESTRUCTORS
! 2227: ************************************************************************/
! 2228:
! 2229: /*
! 2230: * Immediately destroy a control connection and all associated sessions.
! 2231: */
! 2232: void
! 2233: ppp_l2tp_ctrl_destroy(struct ppp_l2tp_ctrl **ctrlp)
! 2234: {
! 2235: struct ppp_l2tp_ctrl *const ctrl = *ctrlp;
! 2236:
! 2237: /* Sanity */
! 2238: if (ctrl == NULL)
! 2239: return;
! 2240: *ctrlp = NULL;
! 2241:
! 2242: /* Destroy all sessions */
! 2243: while (ghash_size(ctrl->sessions) > 0) {
! 2244: struct ppp_l2tp_sess *sess;
! 2245: struct ghash_walk walk;
! 2246:
! 2247: ghash_walk_init(ctrl->sessions, &walk);
! 2248: sess = ghash_walk_next(ctrl->sessions, &walk);
! 2249: ppp_l2tp_sess_destroy(&sess);
! 2250: }
! 2251:
! 2252: /* Destroy netgraph node */
! 2253: (void)NgSendMsg(ctrl->csock, NG_L2TP_HOOK_CTRL,
! 2254: NGM_GENERIC_COOKIE, NGM_SHUTDOWN, NULL, 0);
! 2255:
! 2256: /* Destroy control connection */
! 2257: ghash_remove(ppp_l2tp_ctrls, ctrl);
! 2258: if (ghash_size(ppp_l2tp_ctrls) == 0)
! 2259: ghash_destroy(&ppp_l2tp_ctrls);
! 2260: (void)close(ctrl->csock);
! 2261: (void)close(ctrl->dsock);
! 2262: ppp_log_close(&ctrl->log);
! 2263: pevent_unregister(&ctrl->reply_timer);
! 2264: pevent_unregister(&ctrl->close_timer);
! 2265: pevent_unregister(&ctrl->death_timer);
! 2266: pevent_unregister(&ctrl->idle_timer);
! 2267: pevent_unregister(&ctrl->ctrl_event);
! 2268: pevent_unregister(&ctrl->data_event);
! 2269: ppp_l2tp_avp_list_destroy(&ctrl->avps);
! 2270: ghash_destroy(&ctrl->sessions);
! 2271: FREE(CTRL_MEM_TYPE, ctrl->secret);
! 2272: FREE(CTRL_MEM_TYPE, ctrl->errmsg);
! 2273: memset(ctrl, 0, sizeof(*ctrl));
! 2274: FREE(CTRL_MEM_TYPE, ctrl);
! 2275: }
! 2276:
! 2277: /*
! 2278: * Immediately destroy a session.
! 2279: */
! 2280: static void
! 2281: ppp_l2tp_sess_destroy(struct ppp_l2tp_sess **sessp)
! 2282: {
! 2283: struct ppp_l2tp_sess *const sess = *sessp;
! 2284: struct ppp_l2tp_ctrl *ctrl;
! 2285: char path[32];
! 2286:
! 2287: /* Sanity */
! 2288: if (sess == NULL)
! 2289: return;
! 2290: *sessp = NULL;
! 2291:
! 2292: /* Destroy session */
! 2293: ctrl = sess->ctrl;
! 2294: if (sess->state != SS_DYING)
! 2295: ctrl->active_sessions--;
! 2296: ghash_remove(ctrl->sessions, sess);
! 2297: snprintf(path, sizeof(path), "[%lx]:", (u_long)sess->node_id);
! 2298: (void)NgSendMsg(ctrl->csock, path,
! 2299: NGM_GENERIC_COOKIE, NGM_SHUTDOWN, NULL, 0);
! 2300: ppp_log_close(&sess->log);
! 2301: ppp_l2tp_avp_list_destroy(&sess->my_avps);
! 2302: ppp_l2tp_avp_list_destroy(&sess->peer_avps);
! 2303: pevent_unregister(&sess->notify_timer);
! 2304: pevent_unregister(&sess->reply_timer);
! 2305: pevent_unregister(&sess->close_timer);
! 2306: pevent_unregister(&sess->death_timer);
! 2307: FREE(SESS_MEM_TYPE, sess->errmsg);
! 2308: memset(sess, 0, sizeof(*sess));
! 2309: FREE(SESS_MEM_TYPE, sess);
! 2310: }
! 2311:
! 2312: /************************************************************************
! 2313: HASH TABLE FUNCTIONS
! 2314: ************************************************************************/
! 2315:
! 2316: static int
! 2317: ppp_l2tp_ctrl_equal(struct ghash *g, const void *item1, const void *item2)
! 2318: {
! 2319: const struct ppp_l2tp_ctrl *const ctrl1 = item1;
! 2320: const struct ppp_l2tp_ctrl *const ctrl2 = item2;
! 2321:
! 2322: return (ctrl1->config.tunnel_id == ctrl2->config.tunnel_id);
! 2323: }
! 2324:
! 2325: static u_int32_t
! 2326: ppp_l2tp_ctrl_hash(struct ghash *g, const void *item)
! 2327: {
! 2328: const struct ppp_l2tp_ctrl *const ctrl = item;
! 2329:
! 2330: return ((u_int32_t)ctrl->config.tunnel_id);
! 2331: }
! 2332:
! 2333: static int
! 2334: ppp_l2tp_sess_equal(struct ghash *g, const void *item1, const void *item2)
! 2335: {
! 2336: const struct ppp_l2tp_sess *const sess1 = item1;
! 2337: const struct ppp_l2tp_sess *const sess2 = item2;
! 2338:
! 2339: return (sess1->config.session_id == sess2->config.session_id);
! 2340: }
! 2341:
! 2342: static u_int32_t
! 2343: ppp_l2tp_sess_hash(struct ghash *g, const void *item)
! 2344: {
! 2345: const struct ppp_l2tp_sess *const sess = item;
! 2346:
! 2347: return ((u_int32_t)sess->config.session_id);
! 2348: }
! 2349:
! 2350: /************************************************************************
! 2351: STRING CONVERTERS
! 2352: ************************************************************************/
! 2353:
! 2354: /*
! 2355: * Dump an AVP list.
! 2356: */
! 2357: static void
! 2358: ppp_l2tp_ctrl_dump(struct ppp_l2tp_ctrl *ctrl,
! 2359: struct ppp_l2tp_avp_list *avps, const char *fmt, ...)
! 2360: {
! 2361: char buf[1024];
! 2362: va_list args;
! 2363: int i;
! 2364:
! 2365: va_start(args, fmt);
! 2366: vsnprintf(buf, sizeof(buf), fmt, args);
! 2367: va_end(args);
! 2368: for (i = 0; i < avps->length; i++) {
! 2369: struct ppp_l2tp_avp *const avp = &avps->avps[i];
! 2370: const struct ppp_l2tp_avp_info *info;
! 2371: int j;
! 2372:
! 2373: strlcat(buf, i > 0 ? " [" : "[", sizeof(buf));
! 2374: for (j = 0; (info = &ppp_l2tp_avp_info_list[j])->name != NULL
! 2375: && (info->vendor != avp->vendor
! 2376: || info->type != avp->type); j++);
! 2377: if (info->name != NULL) {
! 2378: strlcat(buf, info->name, sizeof(buf));
! 2379: strlcat(buf, " ", sizeof(buf));
! 2380: (*info->decode)(info, avp,
! 2381: buf + strlen(buf), sizeof(buf) - strlen(buf));
! 2382: } else {
! 2383: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
! 2384: "%u:%u vlen=%u", avp->vendor, avp->type, avp->vlen);
! 2385: }
! 2386: strlcat(buf, "]", sizeof(buf));
! 2387: }
! 2388: CLOG(LOG_DEBUG, "%s", buf);
! 2389: }
! 2390:
! 2391: static const char *
! 2392: ppp_l2tp_ctrl_state_str(enum l2tp_ctrl_state state)
! 2393: {
! 2394: static char buf[32];
! 2395:
! 2396: switch (state) {
! 2397: case CS_IDLE:
! 2398: return ("idle");
! 2399: case CS_WAIT_CTL_REPLY:
! 2400: return ("wait-ctl-reply");
! 2401: case CS_WAIT_CTL_CONNECT:
! 2402: return ("wait-ctl-conn");
! 2403: case CS_ESTABLISHED:
! 2404: return ("established");
! 2405: case CS_DYING:
! 2406: return ("dying");
! 2407: default:
! 2408: snprintf(buf, sizeof(buf), "?%u?", state);
! 2409: return (buf);
! 2410: }
! 2411: }
! 2412:
! 2413: static const char *
! 2414: ppp_l2tp_sess_state_str(enum l2tp_ctrl_state state)
! 2415: {
! 2416: static char buf[32];
! 2417:
! 2418: switch (state) {
! 2419: case SS_WAIT_REPLY:
! 2420: return ("wait-cs-reply");
! 2421: case SS_WAIT_CONNECT:
! 2422: return ("wait-connect");
! 2423: case SS_WAIT_ANSWER:
! 2424: return ("wait-answer");
! 2425: case SS_ESTABLISHED:
! 2426: return ("established");
! 2427: case SS_DYING:
! 2428: return ("dying");
! 2429: default:
! 2430: snprintf(buf, sizeof(buf), "?%u?", state);
! 2431: return (buf);
! 2432: }
! 2433: }
! 2434:
! 2435: static const char *
! 2436: ppp_l2tp_sess_orig_str(enum l2tp_sess_orig orig)
! 2437: {
! 2438: return (orig == ORIG_LOCAL ? "local" : "remote");
! 2439: }
! 2440:
! 2441: static const char *
! 2442: ppp_l2tp_sess_side_str(enum l2tp_sess_side side)
! 2443: {
! 2444: return (side == SIDE_LNS ? "LNS" : "LAC");
! 2445: }
! 2446:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>