diff options
Diffstat (limited to 'accel-pppd/ppp')
-rw-r--r-- | accel-pppd/ppp/ccp_mppe.c | 6 | ||||
-rw-r--r-- | accel-pppd/ppp/ipcp_opt_ipaddr.c | 51 | ||||
-rw-r--r-- | accel-pppd/ppp/ipv6cp_opt_intfid.c | 56 | ||||
-rw-r--r-- | accel-pppd/ppp/ppp.c | 21 | ||||
-rw-r--r-- | accel-pppd/ppp/ppp.h | 2 | ||||
-rw-r--r-- | accel-pppd/ppp/ppp_auth.c | 4 | ||||
-rw-r--r-- | accel-pppd/ppp/ppp_ccp.c | 34 | ||||
-rw-r--r-- | accel-pppd/ppp/ppp_ccp.h | 1 | ||||
-rw-r--r-- | accel-pppd/ppp/ppp_fsm.c | 8 | ||||
-rw-r--r-- | accel-pppd/ppp/ppp_fsm.h | 1 | ||||
-rw-r--r-- | accel-pppd/ppp/ppp_ipcp.c | 130 | ||||
-rw-r--r-- | accel-pppd/ppp/ppp_ipcp.h | 5 | ||||
-rw-r--r-- | accel-pppd/ppp/ppp_ipv6cp.c | 226 | ||||
-rw-r--r-- | accel-pppd/ppp/ppp_ipv6cp.h | 7 |
14 files changed, 409 insertions, 143 deletions
diff --git a/accel-pppd/ppp/ccp_mppe.c b/accel-pppd/ppp/ccp_mppe.c index 7d9aab21..d3e127a6 100644 --- a/accel-pppd/ppp/ccp_mppe.c +++ b/accel-pppd/ppp/ccp_mppe.c @@ -71,7 +71,7 @@ static struct ccp_option_t *mppe_init(struct ppp_ccp_t *ccp) mppe_opt->mppe = -1; if (conf_mppe == 2) - ccp->passive = 0; + ccp->ld.passive = 0; mppe_opt->opt.id = CI_MPPE; mppe_opt->opt.len = 6; @@ -288,7 +288,7 @@ static void ev_mppe_keys(struct ev_mppe_keys_t *ev) if (ev->policy == 2) { mppe_opt->mppe = 1; - ccp->passive = 0; + ccp->ld.passive = 0; } else if (ev->policy == 1) { if (conf_mppe == 1) mppe_opt->mppe = 1; @@ -296,7 +296,7 @@ static void ev_mppe_keys(struct ev_mppe_keys_t *ev) mppe_opt->mppe = -1; if (conf_mppe == 2) - ccp->passive = 1; + ccp->ld.passive = 1; } } diff --git a/accel-pppd/ppp/ipcp_opt_ipaddr.c b/accel-pppd/ppp/ipcp_opt_ipaddr.c index 3a3f6db6..14557440 100644 --- a/accel-pppd/ppp/ipcp_opt_ipaddr.c +++ b/accel-pppd/ppp/ipcp_opt_ipaddr.c @@ -82,31 +82,41 @@ static int check_exists(struct ppp_t *self_ppp, in_addr_t addr) return r; } +static int alloc_ip(struct ppp_t *ppp) +{ + ppp->ipv4 = ipdb_get_ipv4(ppp); + if (!ppp->ipv4) { + log_ppp_warn("ppp: no free IPv4 address\n"); + return IPCP_OPT_CLOSE; + } + + if (iprange_tunnel_check(ppp->ipv4->peer_addr)) { + log_ppp_warn("ppp:ipcp: to avoid kernel soft lockup requested IP cannot be assigned (%i.%i.%i.%i)\n", + ppp->ipv4->peer_addr&0xff, + (ppp->ipv4->peer_addr >> 8)&0xff, + (ppp->ipv4->peer_addr >> 16)&0xff, + (ppp->ipv4->peer_addr >> 24)&0xff); + return IPCP_OPT_FAIL; + } + + if (conf_check_exists && check_exists(ppp, ppp->ipv4->peer_addr)) + return IPCP_OPT_FAIL; + + return 0; +} + static int ipaddr_send_conf_req(struct ppp_ipcp_t *ipcp, struct ipcp_option_t *opt, uint8_t *ptr) { struct ipaddr_option_t *ipaddr_opt = container_of(opt, typeof(*ipaddr_opt), opt); struct ipcp_opt32_t *opt32 = (struct ipcp_opt32_t *)ptr; + int r; if (!ipcp->ppp->ipv4) { - ipcp->ppp->ipv4 = ipdb_get_ipv4(ipcp->ppp); - if (!ipcp->ppp->ipv4) { - log_ppp_warn("ppp: no free IPv4 address\n"); - return -1; - } - } - - if (iprange_tunnel_check(ipcp->ppp->ipv4->peer_addr)) { - log_ppp_warn("ppp:ipcp: to avoid kernel soft lockup requested IP cannot be assigned (%i.%i.%i.%i)\n", - ipcp->ppp->ipv4->peer_addr&0xff, - (ipcp->ppp->ipv4->peer_addr >> 8)&0xff, - (ipcp->ppp->ipv4->peer_addr >> 16)&0xff, - (ipcp->ppp->ipv4->peer_addr >> 24)&0xff); - return -1; + r = alloc_ip(ipcp->ppp); + if (r) + return r; } - if (conf_check_exists && check_exists(ipcp->ppp, ipcp->ppp->ipv4->peer_addr)) - return -1; - opt32->hdr.id = CI_ADDR; opt32->hdr.len = 6; opt32->val = ipcp->ppp->ipv4->addr; @@ -129,6 +139,13 @@ static int ipaddr_recv_conf_req(struct ppp_ipcp_t *ipcp, struct ipcp_option_t *o struct ipcp_opt32_t *opt32 = (struct ipcp_opt32_t *)ptr; struct ifreq ifr; struct sockaddr_in addr; + int r; + + if (!ipcp->ppp->ipv4) { + r = alloc_ip(ipcp->ppp); + if (r) + return r; + } if (opt32->hdr.len != 6) return IPCP_OPT_REJ; diff --git a/accel-pppd/ppp/ipv6cp_opt_intfid.c b/accel-pppd/ppp/ipv6cp_opt_intfid.c index e8681020..363f6c9f 100644 --- a/accel-pppd/ppp/ipv6cp_opt_intfid.c +++ b/accel-pppd/ppp/ipv6cp_opt_intfid.c @@ -11,6 +11,7 @@ #include "log.h" #include "events.h" #include "ppp.h" +#include "ppp_ccp.h" #include "ppp_ipv6cp.h" #include "ipdb.h" @@ -87,6 +88,9 @@ static void ipaddr_free(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_option_t *opt { struct ipaddr_option_t *ipaddr_opt=container_of(opt,typeof(*ipaddr_opt),opt); + if (ipv6cp->ppp->ipv6) + ipdb_put_ipv6(ipv6cp->ppp, ipv6cp->ppp->ipv6); + _free(ipaddr_opt); } @@ -148,30 +152,43 @@ static uint64_t generate_peer_intf_id(struct ppp_t *ppp) u.addr16[i] = htons(n); } } else - read(urandom_fd, &u, sizeof(u)); + return 0; } return u.intf_id; } +static int alloc_ip(struct ppp_t *ppp) +{ + ppp->ipv6 = ipdb_get_ipv6(ppp); + if (!ppp->ipv6) { + log_ppp_warn("ppp: no free IPv6 address\n"); + return IPV6CP_OPT_CLOSE; + } + + if (conf_check_exists && check_exists(ppp)) + return IPV6CP_OPT_FAIL; + + return 0; +} + static int ipaddr_send_conf_req(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_option_t *opt, uint8_t *ptr) { struct ipaddr_option_t *ipaddr_opt = container_of(opt, typeof(*ipaddr_opt), opt); struct ipv6cp_opt64_t *opt64 = (struct ipv6cp_opt64_t *)ptr; + int r; if (!ipv6cp->ppp->ipv6) { - ipv6cp->ppp->ipv6 = ipdb_get_ipv6(ipv6cp->ppp); - if (!ipv6cp->ppp->ipv6) { - log_ppp_warn("ppp: no free IPv6 address\n"); - return -1; - } + r = alloc_ip(ipv6cp->ppp); + if (r) + return r; } - - if (!ipv6cp->ppp->ipv6->intf_id) - ipv6cp->ppp->ipv6->intf_id = generate_peer_intf_id(ipv6cp->ppp); - if (conf_check_exists && check_exists(ipv6cp->ppp)) - return -1; + if (!ipv6cp->ppp->ipv6->intf_id) { + ipv6cp->ppp->ipv6->intf_id = generate_peer_intf_id(ipv6cp->ppp); + if (!ipv6cp->ppp->ipv6->intf_id) + return IPV6CP_OPT_TERMACK; + } opt64->hdr.id = CI_INTFID; opt64->hdr.len = 10; @@ -195,12 +212,25 @@ static int ipaddr_recv_conf_req(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_optio struct ipv6cp_opt64_t *opt64 = (struct ipv6cp_opt64_t* )ptr; struct in6_ifreq ifr6; struct ipv6db_addr_t *a; + int r; + if (!ipv6cp->ppp->ipv6) { + r = alloc_ip(ipv6cp->ppp); + if (r) + return r; + } + + if (!ipv6cp->ppp->ipv6->intf_id) { + ipv6cp->ppp->ipv6->intf_id = generate_peer_intf_id(ipv6cp->ppp); + if (!ipv6cp->ppp->ipv6->intf_id) + return IPV6CP_OPT_TERMACK; + } + if (opt64->hdr.len != 10) return IPV6CP_OPT_REJ; if (ipv6cp->ppp->ipv6->intf_id == opt64->val) { - //ipv6cp->delay_ack = ccp_ipcp_started(ipcp->ppp); + ipv6cp->delay_ack = ccp_ipcp_started(ipv6cp->ppp); goto ack; } @@ -285,7 +315,7 @@ static uint64_t parse_intfid(const char *opt) return u.u64; err: - log_error("ppp:ipv6cp: failed to parse ipv6-intf-id\n"); + log_error("ppp:ipv6cp: failed to parse intf-id '%s'\n", opt); conf_intf_id = INTF_ID_RANDOM; return 0; } diff --git a/accel-pppd/ppp/ppp.c b/accel-pppd/ppp/ppp.c index 875dbc39..3b65dfeb 100644 --- a/accel-pppd/ppp/ppp.c +++ b/accel-pppd/ppp/ppp.c @@ -310,7 +310,7 @@ cont: if (ppp_h->proto == proto) { ppp_h->recv(ppp_h); if (ppp->chan_fd == -1) { - ppp->ctrl->finished(ppp); + //ppp->ctrl->finished(ppp); return 1; } goto cont; @@ -356,7 +356,7 @@ cont: if (ppp_h->proto == proto) { ppp_h->recv(ppp_h); if (ppp->unit_fd == -1) { - ppp->ctrl->finished(ppp); + //ppp->ctrl->finished(ppp); return 1; } goto cont; @@ -428,33 +428,42 @@ static void ppp_ifup(struct ppp_t *ppp) log_ppp_error("ppp: failed to set NP (IPv6) mode: %s\n", strerror(errno)); } + ppp->ctrl->started(ppp); + triton_event_fire(EV_PPP_STARTED, ppp); } void __export ppp_layer_started(struct ppp_t *ppp, struct ppp_layer_data_t *d) { struct layer_node_t *n = d->node; + int f = 0; if (d->started) return; d->started = 1; - list_for_each_entry(d, &n->items, entry) - if (!d->started) return; + list_for_each_entry(d, &n->items, entry) { + if (!d->started && !d->passive) return; + if (d->started && !d->optional) + f = 1; + } + + if (!f) + return; + if (n->entry.next == &ppp->layers) { ppp->state = PPP_STATE_ACTIVE; __sync_sub_and_fetch(&ppp_stat.starting, 1); __sync_add_and_fetch(&ppp_stat.active, 1); - ppp->ctrl->started(ppp); ppp_ifup(ppp); } else { n = list_entry(n->entry.next, typeof(*n), entry); list_for_each_entry(d, &n->items, entry) { d->starting = 1; if (d->layer->start(d)) { - ppp_terminate(ppp, 1, TERM_NAS_ERROR); + ppp_terminate(ppp, TERM_NAS_ERROR, 0); return; } } diff --git a/accel-pppd/ppp/ppp.h b/accel-pppd/ppp/ppp.h index f95893ab..744202e1 100644 --- a/accel-pppd/ppp/ppp.h +++ b/accel-pppd/ppp/ppp.h @@ -132,6 +132,8 @@ struct ppp_layer_data_t struct list_head entry; struct ppp_layer_t *layer; struct layer_node_t *node; + int passive:1; + int optional:1; int starting:1; int started:1; int finished:1; diff --git a/accel-pppd/ppp/ppp_auth.c b/accel-pppd/ppp/ppp_auth.c index b69c7599..29404d38 100644 --- a/accel-pppd/ppp/ppp_auth.c +++ b/accel-pppd/ppp/ppp_auth.c @@ -314,6 +314,10 @@ static void __ppp_auth_started(struct ppp_t *ppp) log_ppp_debug("auth_layer_started\n"); ppp_layer_started(ppp, &ad->ld); + + if (ppp->terminating) + return; + log_ppp_info1("%s: authentication successed\n", ppp->username); triton_event_fire(EV_PPP_AUTHORIZED, ppp); } diff --git a/accel-pppd/ppp/ppp_ccp.c b/accel-pppd/ppp/ppp_ccp.c index 79b0c32d..5b7715a4 100644 --- a/accel-pppd/ppp/ppp_ccp.c +++ b/accel-pppd/ppp/ppp_ccp.c @@ -107,7 +107,8 @@ static struct ppp_layer_data_t *ccp_layer_init(struct ppp_t *ppp) ppp_register_unit_handler(ppp, &ccp->hnd); - ccp->passive = 1; + ccp->ld.passive = 1; + ccp->ld.optional = 1; INIT_LIST_HEAD(&ccp->options); ccp_options_init(ccp); @@ -146,7 +147,7 @@ int ccp_layer_start(struct ppp_layer_data_t *ld) ccp->starting = 1; - if (!ccp->passive) { + if (!ccp->ld.passive) { ppp_fsm_lower_up(&ccp->fsm); if (ppp_fsm_open(&ccp->fsm)) return -1; @@ -208,10 +209,10 @@ static void ccp_layer_finished(struct ppp_fsm_t *fsm) log_ppp_debug("ccp_layer_finished\n"); - if (!ccp->started) { - ccp->started = 1; - ppp_layer_started(ccp->ppp, &ccp->ld); - } + if (!ccp->started) + ccp->ld.passive = 1; + else if (!ccp->ppp->terminating) + ppp_terminate(ccp->ppp, TERM_USER_ERROR, 0); } static void ccp_layer_down(struct ppp_fsm_t *fsm) @@ -223,7 +224,6 @@ static void ccp_layer_down(struct ppp_fsm_t *fsm) ppp_fsm_close(fsm); } - static void print_ropt(struct recv_opt_t *ropt) { int i; @@ -244,7 +244,7 @@ static int send_conf_req(struct ppp_fsm_t *fsm) struct ccp_option_t *lopt; int n; - if (ccp->passive) + if (ccp->ld.passive) return 0; buf = _malloc(ccp->conf_req_len); @@ -648,8 +648,8 @@ static void ccp_recv(struct ppp_handler_t*h) switch(hdr->code) { case CONFREQ: r = ccp_recv_conf_req(ccp, (uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN); - if (ccp->passive) { - ccp->passive = 0; + if (ccp->ld.passive) { + ccp->ld.passive = 0; ppp_fsm_lower_up(&ccp->fsm); ppp_fsm_open(&ccp->fsm); } @@ -673,10 +673,6 @@ static void ccp_recv(struct ppp_handler_t*h) } ccp_free_conf_req(ccp); - /*if (r == CCP_OPT_ACK && ccp->passive) { - ccp->passive = 0; - send_conf_req(&ccp->fsm); - }*/ if (r == CCP_OPT_FAIL) ppp_terminate(ccp->ppp, TERM_USER_ERROR, 0); break; @@ -763,15 +759,7 @@ int ccp_ipcp_started(struct ppp_t *ppp) { struct ppp_ccp_t *ccp = container_of(ppp_find_layer_data(ppp, &ccp_layer), typeof(*ccp), ld); - if (ccp->passive) { - ccp->fsm.fsm_state = FSM_Closed; - ccp->started = 1; - ppp_layer_started(ccp->ppp, &ccp->ld); - - return 0; - } - - return !ccp->started; + return !ccp->ld.passive && !ccp->started; } static struct ppp_layer_t ccp_layer= diff --git a/accel-pppd/ppp/ppp_ccp.h b/accel-pppd/ppp/ppp_ccp.h index 3a48816a..acc56ff0 100644 --- a/accel-pppd/ppp/ppp_ccp.h +++ b/accel-pppd/ppp/ppp_ccp.h @@ -84,7 +84,6 @@ struct ppp_ccp_t int ropt_len; int conf_req_len; - int passive:1; int starting:1; int started:1; }; diff --git a/accel-pppd/ppp/ppp_fsm.c b/accel-pppd/ppp/ppp_fsm.c index b43945b8..c920b5c2 100644 --- a/accel-pppd/ppp/ppp_fsm.c +++ b/accel-pppd/ppp/ppp_fsm.c @@ -106,10 +106,10 @@ int ppp_fsm_open(struct ppp_fsm_t *layer) //if (layer->init_req_cnt) layer->init_req_cnt(layer); init_req_counter(layer,layer->max_configure); --layer->restart_counter; + layer->fsm_state=FSM_Req_Sent; if (layer->send_conf_req) if (layer->send_conf_req(layer)) return -1; - layer->fsm_state=FSM_Req_Sent; break; case FSM_Closing: case FSM_Stopping: @@ -155,6 +155,12 @@ void ppp_fsm_close(struct ppp_fsm_t *layer) } } +void ppp_fsm_close2(struct ppp_fsm_t *layer) +{ + stop_timer(layer); + layer->fsm_state=FSM_Closed; +} + void ppp_fsm_timeout0(struct ppp_fsm_t *layer) { switch(layer->fsm_state) diff --git a/accel-pppd/ppp/ppp_fsm.h b/accel-pppd/ppp/ppp_fsm.h index 6d2fb149..7b7474b3 100644 --- a/accel-pppd/ppp/ppp_fsm.h +++ b/accel-pppd/ppp/ppp_fsm.h @@ -56,6 +56,7 @@ int ppp_fsm_lower_up(struct ppp_fsm_t*); void ppp_fsm_lower_down(struct ppp_fsm_t*); int ppp_fsm_open(struct ppp_fsm_t*); void ppp_fsm_close(struct ppp_fsm_t*); +void ppp_fsm_close2(struct ppp_fsm_t *layer); void ppp_fsm_timeout0(struct ppp_fsm_t *layer); void ppp_fsm_timeout1(struct ppp_fsm_t *layer); void ppp_fsm_recv_conf_req_ack(struct ppp_fsm_t *layer); diff --git a/accel-pppd/ppp/ppp_ipcp.c b/accel-pppd/ppp/ppp_ipcp.c index bd9f50df..af192fe6 100644 --- a/accel-pppd/ppp/ppp_ipcp.c +++ b/accel-pppd/ppp/ppp_ipcp.c @@ -7,6 +7,7 @@ #include "triton.h" #include "log.h" +#include "events.h" #include "ppp.h" #include "ppp_ipcp.h" @@ -22,16 +23,25 @@ struct recv_opt_t struct ipcp_option_t *lopt; }; +#define IPV4_DENY 0 +#define IPV4_ALLOW 1 +#define IPV4_PREFERE 2 +#define IPV4_REQUIRE 3 + +static int conf_ipv4 = IPV4_ALLOW; + static LIST_HEAD(option_handlers); static struct ppp_layer_t ipcp_layer; static void ipcp_layer_up(struct ppp_fsm_t*); static void ipcp_layer_down(struct ppp_fsm_t*); +static void ipcp_layer_finished(struct ppp_fsm_t*); static int send_conf_req(struct ppp_fsm_t*); static void send_conf_ack(struct ppp_fsm_t*); static void send_conf_nak(struct ppp_fsm_t*); static void send_conf_rej(struct ppp_fsm_t*); static void ipcp_recv(struct ppp_handler_t*); +static void ipcp_recv_proto_rej(struct ppp_handler_t*); static void send_term_req(struct ppp_fsm_t *fsm); static void send_term_ack(struct ppp_fsm_t *fsm); @@ -75,6 +85,7 @@ static struct ppp_layer_data_t *ipcp_layer_init(struct ppp_t *ppp) ipcp->hnd.proto = PPP_IPCP; ipcp->hnd.recv = ipcp_recv; + ipcp->hnd.recv_proto_rej = ipcp_recv_proto_rej; ppp_register_unit_handler(ppp, &ipcp->hnd); @@ -82,7 +93,8 @@ static struct ppp_layer_data_t *ipcp_layer_init(struct ppp_t *ppp) ppp_fsm_init(&ipcp->fsm); ipcp->fsm.layer_up = ipcp_layer_up; - ipcp->fsm.layer_finished = ipcp_layer_down; + ipcp->fsm.layer_finished = ipcp_layer_finished; + ipcp->fsm.layer_down = ipcp_layer_down; ipcp->fsm.send_conf_req = send_conf_req; ipcp->fsm.send_conf_ack = send_conf_ack; ipcp->fsm.send_conf_nak = send_conf_nak; @@ -93,6 +105,8 @@ static struct ppp_layer_data_t *ipcp_layer_init(struct ppp_t *ppp) INIT_LIST_HEAD(&ipcp->options); INIT_LIST_HEAD(&ipcp->ropt_list); + ipcp->ld.passive = conf_ipv4 == IPV4_ALLOW; + return &ipcp->ld; } @@ -103,9 +117,14 @@ int ipcp_layer_start(struct ppp_layer_data_t *ld) log_ppp_debug("ipcp_layer_start\n"); ipcp_options_init(ipcp); - ppp_fsm_lower_up(&ipcp->fsm); - if (ppp_fsm_open(&ipcp->fsm)) - return -1; + + ipcp->starting = 1; + + if (!ipcp->ld.passive && conf_ipv4 != IPV4_DENY) { + ppp_fsm_lower_up(&ipcp->fsm); + if (ppp_fsm_open(&ipcp->fsm)) + return -1; + } return 0; } @@ -153,17 +172,28 @@ static void ipcp_layer_up(struct ppp_fsm_t *fsm) __ipcp_layer_up(ipcp); } -static void ipcp_layer_down(struct ppp_fsm_t *fsm) +static void ipcp_layer_finished(struct ppp_fsm_t *fsm) { struct ppp_ipcp_t *ipcp = container_of(fsm, typeof(*ipcp), fsm); log_ppp_debug("ipcp_layer_finished\n"); - if (ipcp->started) { - ipcp->started = 0; - ppp_layer_finished(ipcp->ppp, &ipcp->ld); - } else - ppp_terminate(ipcp->ppp, TERM_NAS_ERROR, 0); + if (!ipcp->started) { + if (conf_ipv4 == IPV4_REQUIRE) + ppp_terminate(ipcp->ppp, TERM_USER_ERROR, 0); + else + ipcp->ld.passive = 1; + } else if (!ipcp->ppp->terminating) + ppp_terminate(ipcp->ppp, TERM_USER_ERROR, 0); +} + +static void ipcp_layer_down(struct ppp_fsm_t *fsm) +{ + struct ppp_ipcp_t *ipcp = container_of(fsm, typeof(*ipcp), fsm); + + log_ppp_debug("ipcp_layer_down\n"); + + ppp_fsm_close(fsm); } static void print_ropt(struct recv_opt_t *ropt) @@ -195,8 +225,16 @@ static int send_conf_req(struct ppp_fsm_t *fsm) list_for_each_entry(lopt, &ipcp->options, entry) { n = lopt->h->send_conf_req(ipcp, lopt, ptr); - if (n < 0) + if (n < 0) { + if (n == IPCP_OPT_TERMACK) + goto out; + if (n == IPCP_OPT_CLOSE && conf_ipv4 != IPV4_REQUIRE) { + ppp_fsm_close2(fsm); + goto out; + } + _free(buf); return -1; + } if (n) { ptr += n; lopt->print = 1; @@ -218,6 +256,7 @@ static int send_conf_req(struct ppp_fsm_t *fsm) ipcp_hdr->len = htons(ptr - buf - 2); ppp_unit_send(ipcp->ppp, ipcp_hdr, ptr - buf); +out: _free(buf); return 0; @@ -244,7 +283,7 @@ static void send_conf_ack(struct ppp_fsm_t *fsm) static void send_conf_nak(struct ppp_fsm_t *fsm) { struct ppp_ipcp_t *ipcp = container_of(fsm, typeof(*ipcp), fsm); - uint8_t *buf = _malloc(ipcp->conf_req_len), *ptr = buf; + uint8_t *buf = _malloc(ipcp->conf_req_len), *ptr = buf, *ptr1; struct ipcp_hdr_t *ipcp_hdr = (struct ipcp_hdr_t*)ptr; struct recv_opt_t *ropt; @@ -260,12 +299,13 @@ static void send_conf_nak(struct ppp_fsm_t *fsm) list_for_each_entry(ropt, &ipcp->ropt_list, entry) { if (ropt->state == IPCP_OPT_NAK) { - if (conf_ppp_verbose) { - log_ppp_info2(" "); - ropt->lopt->h->print(log_ppp_info2, ropt->lopt, NULL); - } + ptr1 = ptr; ptr += ropt->lopt->h->send_conf_nak(ipcp, ropt->lopt, ptr); } + if (conf_ppp_verbose) { + log_ppp_info2(" "); + ropt->lopt->h->print(log_ppp_info2, ropt->lopt, ptr1); + } } if (conf_ppp_verbose) @@ -371,6 +411,17 @@ static int ipcp_recv_conf_req(struct ppp_ipcp_t *ipcp, uint8_t *data, int size) list_for_each_entry(lopt, &ipcp->options, entry) { if (lopt->id == ropt->hdr->id) { r = lopt->h->recv_conf_req(ipcp, lopt, (uint8_t*)ropt->hdr); + if (r == IPCP_OPT_TERMACK) { + send_term_ack(&ipcp->fsm); + return 0; + } + if (r == IPCP_OPT_CLOSE) { + if (conf_ipv4 == IPV4_REQUIRE) + ppp_terminate(ipcp->ppp, TERM_NAS_ERROR, 0); + else + lcp_send_proto_rej(ipcp->ppp, PPP_IPCP); + return 0; + } if (ipcp->ppp->stop_time) return -1; lopt->state = r; @@ -571,9 +622,13 @@ static void ipcp_recv(struct ppp_handler_t*h) int r; int delay_ack = ipcp->delay_ack; - if (ipcp->fsm.fsm_state == FSM_Initial || ipcp->fsm.fsm_state == FSM_Closed || ipcp->ppp->terminating) { + if (!ipcp->starting || ipcp->fsm.fsm_state == FSM_Closed || ipcp->ppp->terminating || conf_ipv4 == IPV4_DENY) { if (conf_ppp_verbose) log_ppp_warn("IPCP: discarding packet\n"); + if (ipcp->ppp->terminating) + return; + if (ipcp->fsm.fsm_state == FSM_Closed || conf_ipv4 == IPV4_DENY) + lcp_send_proto_rej(ipcp->ppp, PPP_IPCP); return; } @@ -600,6 +655,11 @@ static void ipcp_recv(struct ppp_handler_t*h) ipcp_free_conf_req(ipcp); return; } + if (r && ipcp->ld.passive) { + ipcp->ld.passive = 0; + ppp_fsm_lower_up(&ipcp->fsm); + ppp_fsm_open(&ipcp->fsm); + } if (delay_ack && !ipcp->delay_ack) __ipcp_layer_up(ipcp); if (ipcp->started || delay_ack) { @@ -663,6 +723,17 @@ static void ipcp_recv(struct ppp_handler_t*h) } } +static void ipcp_recv_proto_rej(struct ppp_handler_t*h) +{ + struct ppp_ipcp_t *ipcp = container_of(h, typeof(*ipcp), hnd); + + if (ipcp->fsm.fsm_state == FSM_Initial || ipcp->fsm.fsm_state == FSM_Closed) + return; + + ppp_fsm_lower_down(&ipcp->fsm); + ppp_fsm_close(&ipcp->fsm); +} + int ipcp_option_register(struct ipcp_option_handler_t *h) { /*struct ipcp_option_drv_t *p; @@ -697,9 +768,32 @@ static struct ppp_layer_t ipcp_layer = .free = ipcp_layer_free, }; +static void load_config(void) +{ + const char *opt; + + opt = conf_get_opt("ppp", "ipv4"); + if (opt) { + if (!strcmp(opt, "deny")) + conf_ipv4 = IPV4_DENY; + else if (!strcmp(opt, "allow")) + conf_ipv4 = IPV4_ALLOW; + else if (!strcmp(opt, "prefere")) + conf_ipv4 = IPV4_PREFERE; + else if (!strcmp(opt, "require")) + conf_ipv4 = IPV4_REQUIRE; + else + conf_ipv4 = atoi(opt); + } +} + static void ipcp_init(void) { + load_config(); + + triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config); + ppp_register_layer("ipcp", &ipcp_layer); } -DEFINE_INIT(3, ipcp_init); +DEFINE_INIT(4, ipcp_init); diff --git a/accel-pppd/ppp/ppp_ipcp.h b/accel-pppd/ppp/ppp_ipcp.h index f9554618..0ce20e9f 100644 --- a/accel-pppd/ppp/ppp_ipcp.h +++ b/accel-pppd/ppp/ppp_ipcp.h @@ -45,7 +45,9 @@ struct ipcp_opt32_t #define IPCP_OPT_ACK 1 #define IPCP_OPT_NAK -1 #define IPCP_OPT_REJ -2 -#define IPCP_OPT_FAIL -3 +#define IPCP_OPT_CLOSE -3 +#define IPCP_OPT_TERMACK -4 +#define IPCP_OPT_FAIL -5 struct ppp_ipcp_t; struct ipcp_option_handler_t; @@ -87,6 +89,7 @@ struct ppp_ipcp_t int ropt_len; int conf_req_len; + int starting:1; int started:1; int delay_ack:1; }; diff --git a/accel-pppd/ppp/ppp_ipv6cp.c b/accel-pppd/ppp/ppp_ipv6cp.c index a7d2ad2b..4ad1cfbf 100644 --- a/accel-pppd/ppp/ppp_ipv6cp.c +++ b/accel-pppd/ppp/ppp_ipv6cp.c @@ -7,6 +7,7 @@ #include "triton.h" #include "log.h" +#include "events.h" #include "ppp.h" #include "ppp_ipv6cp.h" @@ -22,17 +23,25 @@ struct recv_opt_t struct ipv6cp_option_t *lopt; }; -static int conf_ipv6 = 2; // 0 - disables, 1 - allow, 2 - require +#define IPV6_DENY 0 +#define IPV6_ALLOW 1 +#define IPV6_PREFERE 2 +#define IPV6_REQUIRE 3 + +static int conf_ipv6 = IPV6_ALLOW; static LIST_HEAD(option_handlers); +static struct ppp_layer_t ipv6cp_layer; static void ipv6cp_layer_up(struct ppp_fsm_t*); static void ipv6cp_layer_down(struct ppp_fsm_t*); +static void ipv6cp_layer_finished(struct ppp_fsm_t*); static int send_conf_req(struct ppp_fsm_t*); static void send_conf_ack(struct ppp_fsm_t*); static void send_conf_nak(struct ppp_fsm_t*); static void send_conf_rej(struct ppp_fsm_t*); static void ipv6cp_recv(struct ppp_handler_t*); +static void ipv6cp_recv_proto_rej(struct ppp_handler_t*); static void send_term_req(struct ppp_fsm_t *fsm); static void send_term_ack(struct ppp_fsm_t *fsm); @@ -76,6 +85,7 @@ static struct ppp_layer_data_t *ipv6cp_layer_init(struct ppp_t *ppp) ipv6cp->hnd.proto = PPP_IPV6CP; ipv6cp->hnd.recv = ipv6cp_recv; + ipv6cp->hnd.recv_proto_rej = ipv6cp_recv_proto_rej; ppp_register_unit_handler(ppp, &ipv6cp->hnd); @@ -83,7 +93,8 @@ static struct ppp_layer_data_t *ipv6cp_layer_init(struct ppp_t *ppp) ppp_fsm_init(&ipv6cp->fsm); ipv6cp->fsm.layer_up = ipv6cp_layer_up; - ipv6cp->fsm.layer_finished = ipv6cp_layer_down; + ipv6cp->fsm.layer_finished = ipv6cp_layer_finished; + ipv6cp->fsm.layer_down = ipv6cp_layer_down; ipv6cp->fsm.send_conf_req = send_conf_req; ipv6cp->fsm.send_conf_ack = send_conf_ack; ipv6cp->fsm.send_conf_nak = send_conf_nak; @@ -94,10 +105,8 @@ static struct ppp_layer_data_t *ipv6cp_layer_init(struct ppp_t *ppp) INIT_LIST_HEAD(&ipv6cp->options); INIT_LIST_HEAD(&ipv6cp->ropt_list); - ipv6cp->passive = conf_ipv6 == 1; + ipv6cp->ld.passive = conf_ipv6 == IPV6_ALLOW; - ipv6cp_options_init(ipv6cp); - return &ipv6cp->ld; } @@ -106,15 +115,16 @@ int ipv6cp_layer_start(struct ppp_layer_data_t *ld) struct ppp_ipv6cp_t *ipv6cp = container_of(ld, typeof(*ipv6cp), ld); log_ppp_debug("ipv6cp_layer_start\n"); - - if (!conf_ipv6) { - ppp_layer_started(ipv6cp->ppp, &ipv6cp->ld); - return 0; - } - ppp_fsm_lower_up(&ipv6cp->fsm); - if (ppp_fsm_open(&ipv6cp->fsm)) - return -1; + ipv6cp_options_init(ipv6cp); + + ipv6cp->starting = 1; + + if (!ipv6cp->ld.passive && conf_ipv6 != IPV6_DENY) { + ppp_fsm_lower_up(&ipv6cp->fsm); + if (ppp_fsm_open(&ipv6cp->fsm)) + return -1; + } return 0; } @@ -144,10 +154,8 @@ void ipv6cp_layer_free(struct ppp_layer_data_t *ld) _free(ipv6cp); } -static void ipv6cp_layer_up(struct ppp_fsm_t *fsm) +static void __ipv6cp_layer_up(struct ppp_ipv6cp_t *ipv6cp) { - struct ppp_ipv6cp_t *ipv6cp = container_of(fsm, typeof(*ipv6cp), fsm); - log_ppp_debug("ipv6cp_layer_started\n"); if (!ipv6cp->started) { @@ -156,17 +164,36 @@ static void ipv6cp_layer_up(struct ppp_fsm_t *fsm) } } -static void ipv6cp_layer_down(struct ppp_fsm_t *fsm) +static void ipv6cp_layer_up(struct ppp_fsm_t *fsm) +{ + struct ppp_ipv6cp_t *ipv6cp = container_of(fsm, typeof(*ipv6cp), fsm); + + if (!ipv6cp->delay_ack) + __ipv6cp_layer_up(ipv6cp); +} + +static void ipv6cp_layer_finished(struct ppp_fsm_t *fsm) { struct ppp_ipv6cp_t *ipv6cp = container_of(fsm, typeof(*ipv6cp), fsm); log_ppp_debug("ipv6cp_layer_finished\n"); - if (ipv6cp->started) { - ipv6cp->started = 0; - ppp_layer_finished(ipv6cp->ppp, &ipv6cp->ld); - } else - ppp_terminate(ipv6cp->ppp, TERM_NAS_ERROR, 0); + if (!ipv6cp->started) { + if (conf_ipv6 == IPV6_REQUIRE) + ppp_terminate(ipv6cp->ppp, TERM_USER_ERROR, 0); + else + ipv6cp->ld.passive = 1; + } else if (!ipv6cp->ppp->terminating) + ppp_terminate(ipv6cp->ppp, TERM_USER_ERROR, 0); +} + +static void ipv6cp_layer_down(struct ppp_fsm_t *fsm) +{ + struct ppp_ipv6cp_t *ipv6cp = container_of(fsm, typeof(*ipv6cp), fsm); + + log_ppp_debug("ipv6cp_layer_down\n"); + + ppp_fsm_close(fsm); } static void print_ropt(struct recv_opt_t *ropt) @@ -189,20 +216,25 @@ static int send_conf_req(struct ppp_fsm_t *fsm) struct ipv6cp_option_t *lopt; int n; - if (ipv6cp->passive) - return 0; - ipv6cp_hdr->proto = htons(PPP_IPV6CP); ipv6cp_hdr->code = CONFREQ; - ipv6cp_hdr->id = ++ipv6cp->fsm.id; + ipv6cp_hdr->id = ipv6cp->fsm.id; ipv6cp_hdr->len = 0; ptr += sizeof(*ipv6cp_hdr); list_for_each_entry(lopt, &ipv6cp->options, entry) { n = lopt->h->send_conf_req(ipv6cp, lopt, ptr); - if (n < 0) + if (n < 0) { + if (n == IPV6CP_OPT_TERMACK) + goto out; + if (n == IPV6CP_OPT_CLOSE && conf_ipv6 != IPV6_REQUIRE) { + ppp_fsm_close2(fsm); + goto out; + } + _free(buf); return -1; + } if (n) { ptr += n; lopt->print = 1; @@ -224,6 +256,7 @@ static int send_conf_req(struct ppp_fsm_t *fsm) ipv6cp_hdr->len = htons(ptr - buf - 2); ppp_unit_send(ipv6cp->ppp, ipv6cp_hdr, ptr - buf); +out: _free(buf); return 0; @@ -234,6 +267,11 @@ static void send_conf_ack(struct ppp_fsm_t *fsm) struct ppp_ipv6cp_t *ipv6cp = container_of(fsm, typeof(*ipv6cp), fsm); struct ipv6cp_hdr_t *hdr = (struct ipv6cp_hdr_t*)ipv6cp->ppp->buf; + if (ipv6cp->delay_ack) { + send_term_ack(fsm); + return; + } + hdr->code = CONFACK; if (conf_ppp_verbose) @@ -263,10 +301,10 @@ static void send_conf_nak(struct ppp_fsm_t *fsm) if (ropt->state == IPV6CP_OPT_NAK) { ptr1 = ptr; ptr += ropt->lopt->h->send_conf_nak(ipv6cp, ropt->lopt, ptr); - if (conf_ppp_verbose) { - log_ppp_info2(" "); - ropt->lopt->h->print(log_ppp_info2, ropt->lopt, ptr1); - } + } + if (conf_ppp_verbose) { + log_ppp_info2(" "); + ropt->lopt->h->print(log_ppp_info2, ropt->lopt, ptr1); } } @@ -373,6 +411,17 @@ static int ipv6cp_recv_conf_req(struct ppp_ipv6cp_t *ipv6cp, uint8_t *data, int list_for_each_entry(lopt, &ipv6cp->options, entry) { if (lopt->id == ropt->hdr->id) { r = lopt->h->recv_conf_req(ipv6cp, lopt, (uint8_t*)ropt->hdr); + if (r == IPV6CP_OPT_TERMACK) { + send_term_ack(&ipv6cp->fsm); + return 0; + } + if (r == IPV6CP_OPT_CLOSE) { + if (conf_ipv6 == IPV6_REQUIRE) + ppp_terminate(ipv6cp->ppp, TERM_NAS_ERROR, 0); + else + lcp_send_proto_rej(ipv6cp->ppp, PPP_IPV6CP); + return 0; + } if (ipv6cp->ppp->stop_time) return -1; lopt->state = r; @@ -423,11 +472,11 @@ static int ipv6cp_recv_conf_rej(struct ppp_ipv6cp_t *ipv6cp, uint8_t *data, int if (conf_ppp_verbose) log_ppp_info2("recv [IPV6CP ConfRej id=%x", ipv6cp->fsm.recv_id); - if (ipv6cp->fsm.recv_id != ipv6cp->fsm.id) { + /*if (ipv6cp->fsm.recv_id != ipv6cp->fsm.id) { if (conf_ppp_verbose) log_ppp_info2(": id mismatch ]\n"); return 0; - } + }*/ while (size > 0) { hdr = (struct ipv6cp_opt_hdr_t *)data; @@ -461,11 +510,11 @@ static int ipv6cp_recv_conf_nak(struct ppp_ipv6cp_t *ipv6cp, uint8_t *data, int if (conf_ppp_verbose) log_ppp_info2("recv [IPV6CP ConfNak id=%x", ipv6cp->fsm.recv_id); - if (ipv6cp->fsm.recv_id != ipv6cp->fsm.id) { + /*if (ipv6cp->fsm.recv_id != ipv6cp->fsm.id) { if (conf_ppp_verbose) log_ppp_info2(": id mismatch ]\n"); return 0; - } + }*/ while (size > 0) { hdr = (struct ipv6cp_opt_hdr_t *)data; @@ -501,11 +550,11 @@ static int ipv6cp_recv_conf_ack(struct ppp_ipv6cp_t *ipv6cp, uint8_t *data, int if (conf_ppp_verbose) log_ppp_info2("recv [IPV6CP ConfAck id=%x", ipv6cp->fsm.recv_id); - if (ipv6cp->fsm.recv_id != ipv6cp->fsm.id) { + /*if (ipv6cp->fsm.recv_id != ipv6cp->fsm.id) { if (conf_ppp_verbose) log_ppp_info2(": id mismatch ]\n"); return 0; - } + }*/ while (size > 0) { hdr = (struct ipv6cp_opt_hdr_t *)data; @@ -571,15 +620,15 @@ static void ipv6cp_recv(struct ppp_handler_t*h) struct ipv6cp_hdr_t *hdr; struct ppp_ipv6cp_t *ipv6cp = container_of(h, typeof(*ipv6cp), hnd); int r; + int delay_ack = ipv6cp->delay_ack; - if (!conf_ipv6) { - lcp_send_proto_rej(ipv6cp->ppp, PPP_IPV6CP); - return; - } - - if (ipv6cp->fsm.fsm_state == FSM_Initial || ipv6cp->fsm.fsm_state == FSM_Closed || ipv6cp->ppp->terminating) { + if (!ipv6cp->starting || ipv6cp->fsm.fsm_state == FSM_Closed || ipv6cp->ppp->terminating || conf_ipv6 == IPV6_DENY) { if (conf_ppp_verbose) log_ppp_warn("IPV6CP: discarding packet\n"); + if (ipv6cp->ppp->terminating) + return; + if (ipv6cp->fsm.fsm_state == FSM_Closed || conf_ipv6 == IPV6_DENY) + lcp_send_proto_rej(ipv6cp->ppp, PPP_IPV6CP); return; } @@ -593,8 +642,12 @@ static void ipv6cp_recv(struct ppp_handler_t*h) log_ppp_warn("IPV6CP: short packet received\n"); return; } + + if ((hdr->code == CONFACK || hdr->code == CONFNAK || hdr->code == CONFREJ) && hdr->id != ipv6cp->fsm.id) + return; ipv6cp->fsm.recv_id = hdr->id; + switch(hdr->code) { case CONFREQ: r = ipv6cp_recv_conf_req(ipv6cp,(uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN); @@ -602,24 +655,34 @@ static void ipv6cp_recv(struct ppp_handler_t*h) ipv6cp_free_conf_req(ipv6cp); return; } - switch(r) { - case IPV6CP_OPT_ACK: - ppp_fsm_recv_conf_req_ack(&ipv6cp->fsm); - break; - case IPV6CP_OPT_NAK: - ppp_fsm_recv_conf_req_nak(&ipv6cp->fsm); - break; - case IPV6CP_OPT_REJ: - ppp_fsm_recv_conf_req_rej(&ipv6cp->fsm); - break; + if (r && ipv6cp->ld.passive) { + ipv6cp->ld.passive = 0; + ppp_fsm_lower_up(&ipv6cp->fsm); + ppp_fsm_open(&ipv6cp->fsm); + } + if (delay_ack && !ipv6cp->delay_ack) + __ipv6cp_layer_up(ipv6cp); + if (ipv6cp->started || delay_ack) { + if (r == IPV6CP_OPT_ACK) + send_conf_ack(&ipv6cp->fsm); + else + r = IPV6CP_OPT_FAIL; + } else { + switch(r) { + case IPV6CP_OPT_ACK: + ppp_fsm_recv_conf_req_ack(&ipv6cp->fsm); + break; + case IPV6CP_OPT_NAK: + ppp_fsm_recv_conf_req_nak(&ipv6cp->fsm); + break; + case IPV6CP_OPT_REJ: + ppp_fsm_recv_conf_req_rej(&ipv6cp->fsm); + break; + } } ipv6cp_free_conf_req(ipv6cp); if (r == IPV6CP_OPT_FAIL) ppp_terminate(ipv6cp->ppp, TERM_USER_ERROR, 0); - else if (ipv6cp->passive) { - ipv6cp->passive = 0; - send_conf_req(&ipv6cp->fsm); - } break; case CONFACK: if (ipv6cp_recv_conf_ack(ipv6cp,(uint8_t*)(hdr + 1), ntohs(hdr->len) - PPP_HDRLEN)) @@ -641,6 +704,7 @@ static void ipv6cp_recv(struct ppp_handler_t*h) if (conf_ppp_verbose) log_ppp_info2("recv [IPV6CP TermReq id=%x]\n", hdr->id); ppp_fsm_recv_term_req(&ipv6cp->fsm); + ppp_terminate(ipv6cp->ppp, TERM_USER_REQUEST, 0); break; case TERMACK: if (conf_ppp_verbose) @@ -659,6 +723,17 @@ static void ipv6cp_recv(struct ppp_handler_t*h) } } +static void ipv6cp_recv_proto_rej(struct ppp_handler_t*h) +{ + struct ppp_ipv6cp_t *ipv6cp = container_of(h, typeof(*ipv6cp), hnd); + + if (ipv6cp->fsm.fsm_state == FSM_Initial || ipv6cp->fsm.fsm_state == FSM_Closed) + return; + + ppp_fsm_lower_down(&ipv6cp->fsm); + ppp_fsm_close(&ipv6cp->fsm); +} + int ipv6cp_option_register(struct ipv6cp_option_handler_t *h) { /*struct ipv6cp_option_drv_t *p; @@ -672,6 +747,19 @@ int ipv6cp_option_register(struct ipv6cp_option_handler_t *h) return 0; } +struct ipv6cp_option_t *ipv6cp_find_option(struct ppp_t *ppp, struct ipv6cp_option_handler_t *h) +{ + struct ppp_ipv6cp_t *ipv6cp = container_of(ppp_find_layer_data(ppp, &ipv6cp_layer), typeof(*ipv6cp), ld); + struct ipv6cp_option_t *opt; + + list_for_each_entry(opt, &ipv6cp->options, entry) + if (opt->h == h) + return opt; + + log_emerg("ipv6cp: BUG: option not found\n"); + abort(); +} + static struct ppp_layer_t ipv6cp_layer = { .init = ipv6cp_layer_init, @@ -680,10 +768,32 @@ static struct ppp_layer_t ipv6cp_layer = .free = ipv6cp_layer_free, }; +static void load_config(void) +{ + const char *opt; + + opt = conf_get_opt("ppp", "ipv6"); + if (opt) { + if (!strcmp(opt, "deny")) + conf_ipv6 = IPV6_DENY; + else if (!strcmp(opt, "allow")) + conf_ipv6 = IPV6_ALLOW; + else if (!strcmp(opt, "prefere")) + conf_ipv6 = IPV6_PREFERE; + else if (!strcmp(opt, "require")) + conf_ipv6 = IPV6_REQUIRE; + else + conf_ipv6 = atoi(opt); + } +} + static void ipv6cp_init(void) { + load_config(); + + triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config); + ppp_register_layer("ipv6cp", &ipv6cp_layer); } DEFINE_INIT(4, ipv6cp_init); - diff --git a/accel-pppd/ppp/ppp_ipv6cp.h b/accel-pppd/ppp/ppp_ipv6cp.h index e688e6c0..2e5c201f 100644 --- a/accel-pppd/ppp/ppp_ipv6cp.h +++ b/accel-pppd/ppp/ppp_ipv6cp.h @@ -53,7 +53,9 @@ struct ipv6cp_opt64_t #define IPV6CP_OPT_ACK 1 #define IPV6CP_OPT_NAK -1 #define IPV6CP_OPT_REJ -2 -#define IPV6CP_OPT_FAIL -3 +#define IPV6CP_OPT_CLOSE -3 +#define IPV6CP_OPT_TERMACK -4 +#define IPV6CP_OPT_FAIL -5 struct ppp_ipv6cp_t; struct ipv6cp_option_handler_t; @@ -95,8 +97,9 @@ struct ppp_ipv6cp_t int ropt_len; int conf_req_len; + int starting:1; int started:1; - int passive:1; + int delay_ack:1; }; int ipv6cp_option_register(struct ipv6cp_option_handler_t *h); |