sai_status_t mlnx_sched_hierarchy_foreach(mlnx_port_config_t *port, mlnx_sched_obj_iter_t iter, mlnx_sched_iter_ctx_t *ctx) { mlnx_qos_queue_config_t *queue; mlnx_iter_ret_t ret; uint32_t ii, lvl; assert(iter != NULL); for (lvl = 0; lvl < MAX_SCHED_LEVELS; lvl++) { for (ii = 0; ii < level_max_groups(lvl); ii++) { ret = iter(port, group_get(port, lvl, ii), ctx); if (ret == ITER_STOP) { goto out; } } } port_queues_foreach(port, queue, ii) { ret = iter(port, &queue->sched_obj, ctx); if (ret == ITER_STOP) { goto out; } }
void prot_group_download_labels_reply( struct qqclient* qq, qqpacket* p ) { bytebuffer *buf = p->buf; uchar cmd = get_byte( buf ); if( cmd == 0x1F ){ //download uint next_pos = get_int( buf ); if( next_pos == 0x1000000 ){ //no group labels info ?? return; } if( next_pos != 0x00 ){ DBG("next_pos == 0x%x", next_pos ); } get_byte( buf ); //unknown get_word( buf ); uchar len; while( buf->pos < buf->len ){ uchar number = get_byte( buf ); get_byte( buf ); len = get_byte( buf ); //temp seems to be utf8 code qqgroup *g = group_get( qq, number, 1 ); if( g == NULL ) continue; memset( g->name, 0, NICKNAME_LEN ); get_data( buf, (uchar*)g->name, len ); // DBG("group id: %u name: %s", g->number, g->name ); } group_put_event( qq ); buddy_put_event( qq ); }else{ DBG("unknown cmd=%x", cmd ); } }
/* * Accept a set of transforms offered by the initiator and chose one we can * handle. */ int ike_phase_1_responder_recv_SA (struct message *msg) { struct exchange *exchange = msg->exchange; struct sa *sa = TAILQ_FIRST (&exchange->sa_list); struct ipsec_sa *isa = sa->data; struct payload *sa_p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA]); struct payload *prop = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_PROPOSAL]); struct ipsec_exch *ie = exchange->data; /* Mark the SA as handled. */ sa_p->flags |= PL_MARK; /* IKE requires that only one SA with only one proposal exists. */ if (TAILQ_NEXT (sa_p, link) || TAILQ_NEXT (prop, link)) { log_print ("ike_phase_1_responder_recv_SA: " "multiple SA or proposal payloads in phase 1"); /* XXX Is there a better notification type? */ message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); return -1; } /* Chose a transform from the SA. */ if (message_negotiate_sa (msg, ike_phase_1_validate_prop) || !TAILQ_FIRST (&sa->protos)) return -1; /* XXX Move into message_negotiate_sa? */ ipsec_decode_transform (msg, sa, TAILQ_FIRST (&sa->protos), TAILQ_FIRST (&sa->protos)->chosen->p); ie->group = group_get (isa->group_desc); /* * Check that the mandatory attributes: encryption, hash, authentication * method and Diffie-Hellman group description, has been supplied. */ if (!exchange->crypto || !ie->hash || !ie->ike_auth || !ie->group) { message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); return -1; } /* Save the body for later hash computation. */ ie->sa_i_b_len = GET_ISAKMP_GEN_LENGTH (sa_p->p) - ISAKMP_GEN_SZ; ie->sa_i_b = malloc (ie->sa_i_b_len); if (!ie->sa_i_b) { /* XXX How to notify peer? */ log_error ("ike_phase_1_responder_recv_SA: malloc (%lu) failed", (unsigned long)ie->sa_i_b_len); return -1; } memcpy (ie->sa_i_b, sa_p->p + ISAKMP_GEN_SZ, ie->sa_i_b_len); return 0; }
int main(void) { int len, id; char buf[DH_MAXSZ], buf2[DH_MAXSZ]; char sec[DH_MAXSZ], sec2[DH_MAXSZ]; struct group *group, *group2; const char *name[] = { "MODP", "EC2N", "ECP" }; group_init(); for (id = 0; id < 0xff; id++) { if ((group = group_get(id)) == NULL || (group2 = group_get(id)) == NULL) continue; printf ("Testing group %d (%s%d): ", id, name[group->spec->type], group->spec->bits); len = dh_getlen(group); dh_create_exchange(group, buf); dh_create_exchange(group2, buf2); dh_create_shared(group, sec, buf2); dh_create_shared(group2, sec2, buf); if (memcmp (sec, sec2, len)) { printf("FAILED\n"); return (1); } else printf("OKAY\n"); group_free(group); group_free(group2); } return (0); }
/* Figure out what transform the responder chose. */ int ike_phase_1_initiator_recv_SA (struct message *msg) { struct exchange *exchange = msg->exchange; struct sa *sa = TAILQ_FIRST (&exchange->sa_list); struct ipsec_exch *ie = exchange->data; struct ipsec_sa *isa = sa->data; struct payload *sa_p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA]); struct payload *prop = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_PROPOSAL]); struct payload *xf = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_TRANSFORM]); /* * IKE requires that only one SA with only one proposal exists and since * we are getting an answer on our transform offer, only one transform. */ if (TAILQ_NEXT (sa_p, link) || TAILQ_NEXT (prop, link) || TAILQ_NEXT (xf, link)) { log_print ("ike_phase_1_initiator_recv_SA: " "multiple SA, proposal or transform payloads in phase 1"); /* XXX Is there a better notification type? */ message_drop (msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); return -1; } /* Check that the chosen transform matches an offer. */ if (message_negotiate_sa (msg, ike_phase_1_validate_prop) || !TAILQ_FIRST (&sa->protos)) return -1; ipsec_decode_transform (msg, sa, TAILQ_FIRST (&sa->protos), xf->p); /* XXX I don't like exchange-specific stuff in here. */ if (exchange->type != ISAKMP_EXCH_AGGRESSIVE) ie->group = group_get (isa->group_desc); /* Mark the SA as handled. */ sa_p->flags |= PL_MARK; return 0; }
int ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, off_t offset) { struct ikev2_notify *n; u_int8_t *buf, md[SHA_DIGEST_LENGTH]; size_t len; u_int32_t spi32; u_int64_t spi64; struct iked_spi *rekey; u_int16_t type; u_int16_t group; if ((n = ibuf_seek(msg->msg_data, offset, sizeof(*n))) == NULL) return (-1); type = betoh16(n->n_type); log_debug("%s: protoid %s spisize %d type %s", __func__, print_map(n->n_protoid, ikev2_saproto_map), n->n_spisize, print_map(type, ikev2_n_map)); len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(*n); if ((buf = ibuf_seek(msg->msg_data, offset + sizeof(*n), len)) == NULL) return (-1); print_hex(buf, 0, len); if (!ikev2_msg_frompeer(msg)) return (0); switch (type) { case IKEV2_N_NAT_DETECTION_SOURCE_IP: case IKEV2_N_NAT_DETECTION_DESTINATION_IP: if (ikev2_nat_detection(env, msg, md, sizeof(md), type) == -1) return (-1); if (len != sizeof(md) || memcmp(buf, md, len) != 0) { log_debug("%s: %s detected NAT, enabling " "UDP encapsulation", __func__, print_map(type, ikev2_n_map)); /* * Enable UDP encapsulation of ESP packages if * the check detected NAT. */ if (msg->msg_sa != NULL) msg->msg_sa->sa_udpencap = 1; } print_hex(md, 0, sizeof(md)); break; case IKEV2_N_INVALID_KE_PAYLOAD: if (len != sizeof(group)) { log_debug("%s: malformed notification", __func__); return (-1); } if (!msg->msg_sa->sa_hdr.sh_initiator) { log_debug("%s: not an initiator", __func__); sa_free(env, msg->msg_sa); msg->msg_sa = NULL; return (-1); } memcpy(&group, buf, len); group = betoh16(group); if ((msg->msg_policy->pol_peerdh = group_get(group)) == NULL) { log_debug("%s: unable to select DH group %d", __func__, group); return (-1); } log_debug("%s: responder selected DH group %d", __func__, group); sa_free(env, msg->msg_sa); msg->msg_sa = NULL; timer_initialize(env, &env->sc_inittmr, ikev2_init_ike_sa, NULL); timer_register(env, &env->sc_inittmr, IKED_INITIATOR_INITIAL); break; case IKEV2_N_NO_ADDITIONAL_SAS: /* This makes sense for Child SAs only atm */ if (msg->msg_sa->sa_stateflags & IKED_REQ_CHILDSA) { ikev2_disable_rekeying(env, msg->msg_sa); msg->msg_sa->sa_stateflags &= ~IKED_REQ_CHILDSA; } break; case IKEV2_N_REKEY_SA: if (len != n->n_spisize) { log_debug("%s: malformed notification", __func__); return (-1); } rekey = &msg->msg_parent->msg_rekey; if (rekey->spi != 0) { log_debug("%s: rekeying of multiple SAs not supported", __func__); return (-1); } switch (n->n_spisize) { case 4: memcpy(&spi32, buf, len); rekey->spi = betoh32(spi32); break; case 8: memcpy(&spi64, buf, len); rekey->spi = betoh64(spi64); break; default: log_debug("%s: invalid spi size %d", __func__, n->n_spisize); return (-1); } rekey->spi_size = n->n_spisize; rekey->spi_protoid = n->n_protoid; log_debug("%s: rekey %s spi %s", __func__, print_map(n->n_protoid, ikev2_saproto_map), print_spi(rekey->spi, n->n_spisize)); break; } return (0); }
/* Offer a set of transforms to the responder in the MSG message. */ int ike_phase_1_initiator_send_SA (struct message *msg) { struct exchange *exchange = msg->exchange; struct ipsec_exch *ie = exchange->data; u_int8_t *proposal = 0, *sa_buf = 0, *saved_nextp, *attr; u_int8_t **transform = 0; size_t transforms_len = 0, proposal_len, sa_len; size_t *transform_len = 0; struct conf_list *conf, *life_conf; struct conf_list_node *xf, *life; int i, value, update_nextp; struct payload *p; struct proto *proto; int group_desc = -1, new_group_desc; /* Get the list of transforms. */ conf = conf_get_list (exchange->policy, "Transforms"); if (!conf) return -1; transform = calloc (conf->cnt, sizeof *transform); if (!transform) { log_error ("ike_phase_1_initiator_send_SA: calloc (%d, %lu) failed", conf->cnt, (unsigned long)sizeof *transform); goto bail_out; } transform_len = calloc (conf->cnt, sizeof *transform_len); if (!transform_len) { log_error ("ike_phase_1_initiator_send_SA: calloc (%d, %lu) failed", conf->cnt, (unsigned long)sizeof *transform_len); goto bail_out; } for (xf = TAILQ_FIRST (&conf->fields), i = 0; i < conf->cnt; i++, xf = TAILQ_NEXT (xf, link)) { /* XXX The sizing needs to be dynamic. */ transform[i] = malloc (ISAKMP_TRANSFORM_SA_ATTRS_OFF + 16 * ISAKMP_ATTR_VALUE_OFF); if (!transform[i]) { log_error ("ike_phase_1_initiator_send_SA: malloc (%d) failed", ISAKMP_TRANSFORM_SA_ATTRS_OFF + 16 * ISAKMP_ATTR_VALUE_OFF); goto bail_out; } SET_ISAKMP_TRANSFORM_NO (transform[i], i); SET_ISAKMP_TRANSFORM_ID (transform[i], IPSEC_TRANSFORM_KEY_IKE); SET_ISAKMP_TRANSFORM_RESERVED (transform[i], 0); attr = transform[i] + ISAKMP_TRANSFORM_SA_ATTRS_OFF; if (attribute_set_constant (xf->field, "ENCRYPTION_ALGORITHM", ike_encrypt_cst, IKE_ATTR_ENCRYPTION_ALGORITHM, &attr)) goto bail_out; if (attribute_set_constant (xf->field, "HASH_ALGORITHM", ike_hash_cst, IKE_ATTR_HASH_ALGORITHM, &attr)) goto bail_out; if (attribute_set_constant (xf->field, "AUTHENTICATION_METHOD", ike_auth_cst, IKE_ATTR_AUTHENTICATION_METHOD, &attr)) goto bail_out; if (attribute_set_constant (xf->field, "GROUP_DESCRIPTION", ike_group_desc_cst, IKE_ATTR_GROUP_DESCRIPTION, &attr)) { /* * If no group description exists, try looking for a user-defined * one. */ if (attribute_set_constant (xf->field, "GROUP_TYPE", ike_group_cst, IKE_ATTR_GROUP_TYPE, &attr)) goto bail_out; #if 0 if (attribute_set_bignum (xf->field, "GROUP_PRIME", IKE_ATTR_GROUP_PRIME, &attr)) goto bail_out; if (attribute_set_bignum (xf->field, "GROUP_GENERATOR_2", IKE_ATTR_GROUP_GENERATOR_2, &attr)) goto bail_out; if (attribute_set_bignum (xf->field, "GROUP_GENERATOR_2", IKE_ATTR_GROUP_GENERATOR_2, &attr)) goto bail_out; if (attribute_set_bignum (xf->field, "GROUP_CURVE_A", IKE_ATTR_GROUP_CURVE_A, &attr)) goto bail_out; if (attribute_set_bignum (xf->field, "GROUP_CURVE_B", IKE_ATTR_GROUP_CURVE_B, &attr)) goto bail_out; #endif } /* * Life durations are special, we should be able to specify * several, one per type. */ life_conf = conf_get_list (xf->field, "Life"); if (life_conf) { for (life = TAILQ_FIRST (&life_conf->fields); life; life = TAILQ_NEXT (life, link)) { attribute_set_constant (life->field, "LIFE_TYPE", ike_duration_cst, IKE_ATTR_LIFE_TYPE, &attr); /* XXX Deals with 16 and 32 bit lifetimes only */ value = conf_get_num (life->field, "LIFE_DURATION", 0); if (value) { if (value <= 0xffff) attr = attribute_set_basic (attr, IKE_ATTR_LIFE_DURATION, value); else { value = htonl (value); attr = attribute_set_var (attr, IKE_ATTR_LIFE_DURATION, (u_int8_t *)&value, sizeof value); } } } conf_free_list (life_conf); } attribute_set_constant (xf->field, "PRF", ike_prf_cst, IKE_ATTR_PRF, &attr); value = conf_get_num (xf->field, "KEY_LENGTH", 0); if (value) attr = attribute_set_basic (attr, IKE_ATTR_KEY_LENGTH, value); value = conf_get_num (xf->field, "FIELD_SIZE", 0); if (value) attr = attribute_set_basic (attr, IKE_ATTR_FIELD_SIZE, value); value = conf_get_num (xf->field, "GROUP_ORDER", 0); if (value) attr = attribute_set_basic (attr, IKE_ATTR_GROUP_ORDER, value); /* Record the real transform size. */ transforms_len += transform_len[i] = attr - transform[i]; /* XXX I don't like exchange-specific stuff in here. */ if (exchange->type == ISAKMP_EXCH_AGGRESSIVE) { /* * Make sure that if a group description is specified, it is * specified for all transforms equally. */ attr = (u_int8_t *)conf_get_str (xf->field, "GROUP_DESCRIPTION"); new_group_desc = attr ? constant_value (ike_group_desc_cst, (char *)attr) : 0; if (group_desc == -1) group_desc = new_group_desc; else if (group_desc != new_group_desc) { log_print ("ike_phase_1_initiator_send_SA: " "differing group descriptions in a proposal"); goto bail_out; } } /* We need to check that we actually support our configuration. */ if (attribute_map (transform[i] + ISAKMP_TRANSFORM_SA_ATTRS_OFF, transform_len[i] - ISAKMP_TRANSFORM_SA_ATTRS_OFF, exchange->doi->is_attribute_incompatible, msg)) { log_print ("ike_phase_1_initiator_send_SA: " "section [%s] has unsupported attribute(s)", xf->field); goto bail_out; } } /* XXX I don't like exchange-specific stuff in here. */ if (exchange->type == ISAKMP_EXCH_AGGRESSIVE) ie->group = group_get (group_desc); proposal_len = ISAKMP_PROP_SPI_OFF; proposal = malloc (proposal_len); if (!proposal) { log_error ("ike_phase_1_initiator_send_SA: malloc (%lu) failed", (unsigned long)proposal_len); goto bail_out; } SET_ISAKMP_PROP_NO (proposal, 1); SET_ISAKMP_PROP_PROTO (proposal, ISAKMP_PROTO_ISAKMP); SET_ISAKMP_PROP_SPI_SZ (proposal, 0); SET_ISAKMP_PROP_NTRANSFORMS (proposal, conf->cnt); /* XXX I would like to see this factored out. */ proto = calloc (1, sizeof *proto); if (!proto) { log_error ("ike_phase_1_initiator_send_SA: calloc (1, %lu) failed", (unsigned long)sizeof *proto); goto bail_out; } proto->no = 1; proto->proto = ISAKMP_PROTO_ISAKMP; proto->sa = TAILQ_FIRST (&exchange->sa_list); TAILQ_INSERT_TAIL (&TAILQ_FIRST (&exchange->sa_list)->protos, proto, link); sa_len = ISAKMP_SA_SIT_OFF + IPSEC_SIT_SIT_LEN; sa_buf = malloc (sa_len); if (!sa_buf) { log_error ("ike_phase_1_initiator_send_SA: malloc (%lu) failed", (unsigned long)sa_len); goto bail_out; } SET_ISAKMP_SA_DOI (sa_buf, IPSEC_DOI_IPSEC); SET_IPSEC_SIT_SIT (sa_buf + ISAKMP_SA_SIT_OFF, IPSEC_SIT_IDENTITY_ONLY); /* * Add the payloads. As this is a SA, we need to recompute the * lengths of the payloads containing others. */ if (message_add_payload (msg, ISAKMP_PAYLOAD_SA, sa_buf, sa_len, 1)) goto bail_out; SET_ISAKMP_GEN_LENGTH (sa_buf, sa_len + proposal_len + transforms_len); sa_buf = 0; saved_nextp = msg->nextp; if (message_add_payload (msg, ISAKMP_PAYLOAD_PROPOSAL, proposal, proposal_len, 0)) goto bail_out; SET_ISAKMP_GEN_LENGTH (proposal, proposal_len + transforms_len); proposal = 0; update_nextp = 0; for (i = 0; i < conf->cnt; i++) { if (message_add_payload (msg, ISAKMP_PAYLOAD_TRANSFORM, transform[i], transform_len[i], update_nextp)) goto bail_out; update_nextp = 1; transform[i] = 0; } msg->nextp = saved_nextp; /* Save SA payload body in ie->sa_i_b, length ie->sa_i_b_len. */ ie->sa_i_b_len = sa_len + proposal_len + transforms_len - ISAKMP_GEN_SZ; ie->sa_i_b = malloc (ie->sa_i_b_len); if (!ie->sa_i_b) { log_error ("ike_phase_1_initiator_send_SA: malloc (%lu) failed", (unsigned long)ie->sa_i_b_len); goto bail_out; } memcpy (ie->sa_i_b, TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA])->p + ISAKMP_GEN_SZ, sa_len - ISAKMP_GEN_SZ); memcpy (ie->sa_i_b + sa_len - ISAKMP_GEN_SZ, TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_PROPOSAL])->p, proposal_len); transforms_len = 0; for (i = 0, p = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_TRANSFORM]); i < conf->cnt; i++, p = TAILQ_NEXT (p, link)) { memcpy (ie->sa_i_b + sa_len + proposal_len + transforms_len - ISAKMP_GEN_SZ, p->p, transform_len[i]); transforms_len += transform_len[i]; } conf_free_list (conf); free (transform); free (transform_len); return 0; bail_out: if (sa_buf) free (sa_buf); if (proposal) free (proposal); if (transform) { for (i = 0; i < conf->cnt; i++) if (transform[i]) free (transform[i]); free (transform); } if (transform_len) free (transform_len); conf_free_list (conf); return -1; }
int conf_group_set(struct ipc_user *u, char *data, size_t len, __unused struct blob_buf *reply) { conf conf = container_of(u, conf_s, ipc_users[CONF_IPC_GROUP_SET]); ifgroups igs = conf->igs; pim pim = conf->pim; struct blob_attr *tb[CONF_G_MAX]; struct in6_addr grp, src; group g = NULL; source s = NULL; gsource gs = NULL; iface i = NULL; ifgroup ig = NULL; ifgsource ifgs = NULL; int join = 0, listen = 0, local = 0; char *str; int ret = 0; if(blobmsg_parse(conf_g_attrs, CONF_G_MAX, tb, data, len) || !tb[CONF_G_GROUP] || !addr_pton(&grp, blobmsg_get_string(tb[CONF_G_GROUP])) || !addr_is_multicast(&grp) || (tb[CONF_G_SRC] && !addr_pton(&src, blobmsg_get_string(tb[CONF_G_SRC]))) || (tb[CONF_G_LISTENER] && !tb[CONF_G_DEV])) return -EINVAL; if(tb[CONF_G_PIM]) { if(!(str = blobmsg_get_string(tb[CONF_G_PIM]))) return -EINVAL; else if (!strcmp(str, "join")) join = PIM_JOIN; else if(!strcmp(str, "prune")) join = PIM_PRUNE; else if(!strcmp(str, "none")) join = PIM_NONE; else return -EINVAL; } if(tb[CONF_G_LISTENER]) { if(!(str = blobmsg_get_string(tb[CONF_G_LISTENER]))) return -EINVAL; else if (!strcmp(str, "include")) listen = PIM_JOIN; else if(!strcmp(str, "exclude")) listen = PIM_PRUNE; else if(!strcmp(str, "none")) listen = PIM_NONE; else return -EINVAL; } if(tb[CONF_G_LOCAL]) { if(!(str = blobmsg_get_string(tb[CONF_G_LOCAL]))) return -EINVAL; else if (!strcmp(str, "include")) local = PIM_JOIN; else if(!strcmp(str, "exclude")) local = PIM_PRUNE; else if(!strcmp(str, "none")) local = PIM_NONE; else return -EINVAL; } if((tb[CONF_G_LOCAL] && !tb[CONF_G_DEV]) || (tb[CONF_G_LISTENER] && !tb[CONF_G_DEV])) return -EINVAL; if((tb[CONF_G_GROUP] && (!(g = group_get(igs, &grp, 1)) || !group_ref(g))) || (tb[CONF_G_SRC] && ((!(s = source_get(igs, &src, 1)) || !group_ref(s)) || (!(gs = gsource_get(g, s, 1)) || !gsource_ref(gs)))) || (tb[CONF_G_DEV] && ((!(i = iface_get_byname(igs, blobmsg_get_string(tb[CONF_G_DEV]), 1)) || !iface_ref(i)) || (!(ig = ifgroup_get(i, g, 1)) || !ifgroup_ref(ig)))) || (ig && gs && (!(ifgs = ifgsource_get(ig, gs, 1)) || !ifgsource_ref(ifgs)))) { ret = -ENOMEM; goto out; } if(tb[CONF_G_PIM]) { if(gs) { L_INFO("Set configuration of gsource "GSOURCE_L" - pim_join_desired : %s", GSOURCE_LA(gs), PIM_STATE_STR(join)); if(!gs->conf_join_desired) gsource_ref(gs); gs->conf_join_desired = join; pim_gsource_conf_changed(pim, gs); if(!gs->conf_join_desired) gsource_unref(gs); } else { L_INFO("Set configuration of group "GROUP_L" - pim_join_desired : %s", GROUP_LA(g), PIM_STATE_STR(join)); if(!g->conf_join_desired) group_ref(g); g->conf_join_desired = join; pim_group_conf_changed(pim, g); if(!g->conf_join_desired) group_unref(g); } } if(tb[CONF_G_LOCAL]) { L_INFO("Set configuration of ifgroup "IFGROUP_L" - local_exclude : %d", IFGROUP_LA(ig), (local == PIM_PRUNE)); if(!ig->conf_local_exclude) ifgroup_ref(ig); ig->conf_local_exclude = !!(local == PIM_PRUNE); pim_ifgroup_conf_changed(pim, ig); if(!ig->conf_local_exclude) ifgroup_unref(ig); } if (tb[CONF_G_LISTENER]) { if(ifgs) { listener_update_G_S(ifgs, LISTENER_CONF, listen == PIM_JOIN, listen == PIM_PRUNE); } else { listener_update_G(ig, LISTENER_CONF, listen == PIM_PRUNE); } } out: if(ifgs) ifgsource_unref(ifgs); if(gs) gsource_unref(gs); if(ig) ifgroup_unref(ig); if(g) group_unref(g); if(s) source_unref(s); if(i) iface_unref(i); return ret; }