Beispiel #1
0
/*
 *    Routing (variable)
 *
 *      The Routing field is optional and is present only if the Routing
 *      Present bit is set to 1.
 *
 *      The Routing field is a list of Source Route Entries (SREs).  Each
 *      SRE has the form:
 *
 *    0                   1                   2                   3
 *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *   |       Address Family          |  SRE Offset   |  SRE Length   |
 *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *   |                        Routing Information ...
 *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 */
libnet_ptag_t
libnet_build_gre_sre(uint16_t af, uint8_t offset, uint8_t length, 
uint8_t *routing, const uint8_t *payload, uint32_t payload_s, libnet_t *l,
libnet_ptag_t ptag)
{
    uint32_t n;
    libnet_pblock_t *p;
    struct libnet_gre_sre_hdr sre_hdr;

    if (l == NULL)
    { 
        return (-1); 
    }

    n = LIBNET_GRE_SRE_H + length + payload_s;

    /*
     *  Find the existing protocol block if a ptag is specified, or create
     *  a new one.
     */
    p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_GRE_SRE_H);
    if (p == NULL)
    {
        return (-1);
    }
    sre_hdr.af = htons(af);
    sre_hdr.sre_offset = offset;
    sre_hdr.sre_length = length;
    n = libnet_pblock_append(l, p, (uint8_t *)&sre_hdr, LIBNET_GRE_SRE_H);
    if (n == -1)
    {
        /* err msg set in libnet_pblock_append() */
        goto bad; 
    }

    if ((routing && !length) || (!routing && length))
    {
        sprintf(l->err_buf, "%s(): routing inconsistency", __func__);
        goto bad;
    }

    if (routing && length)
    {
        n = libnet_pblock_append(l, p, routing, length);
        if (n == -1)
        {
            /* err msg set in libnet_pblock_append() */
            goto bad;
        }
    }

    /* boilerplate payload sanity check / append macro */
    LIBNET_DO_PAYLOAD(l, p);

    return (ptag ? ptag : libnet_pblock_update(l, p, 0, 
           LIBNET_PBLOCK_GRE_SRE_H));

bad:
    libnet_pblock_delete(l, p);
    return (-1);

}
libnet_ptag_t
libnet_build_dnsv4(uint16_t h_len, uint16_t id, uint16_t flags,
            uint16_t num_q, uint16_t num_anws_rr, uint16_t num_auth_rr,
            uint16_t num_addi_rr, const uint8_t *payload, uint32_t payload_s,
            libnet_t *l, libnet_ptag_t ptag)
{

    uint32_t n, h;
    uint offset;
    libnet_pblock_t *p;
    struct libnet_dnsv4_hdr dns_hdr;

    if (l == NULL)
    { 
        return (-1);
    } 

    if (h_len != LIBNET_UDP_DNSV4_H && h_len != LIBNET_TCP_DNSV4_H)
    {
        snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
                "%s(): invalid header length: %d", __func__, h_len);
       return (-1);
    }
    offset = (h_len == LIBNET_UDP_DNSV4_H ? sizeof(dns_hdr.h_len) : 0);
    n = h_len + payload_s;
    h = 0;          /* no checksum */

    /*
     *  Find the existing protocol block if a ptag is specified, or create
     *  a new one.
     */
    p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_DNSV4_H);
    if (p == NULL)
    {
        return (-1);
    }

    /*
     * The sizeof(dns_hdr.h_len) is not counted is the packet size
     * for TCP packet.
     * And since this will be ignored for udp packets, let's compute it 
     * anyway.
     */
    memset(&dns_hdr, 0, sizeof(dns_hdr));
    dns_hdr.h_len       = htons(n - sizeof (dns_hdr.h_len));
    dns_hdr.id          = htons(id);
    dns_hdr.flags       = htons(flags);
    dns_hdr.num_q       = htons(num_q);
    dns_hdr.num_answ_rr = htons(num_anws_rr);
    dns_hdr.num_auth_rr = htons(num_auth_rr);
    dns_hdr.num_addi_rr = htons(num_addi_rr);

    /*
     * A dirty trick: DNS can be either TCP or UDP based, and depending on
     * that, the header changes. A 'length' field is present in TCP packets,
     * but not in UDP packets. As they are the first 2 bytes of the header,
     * they are skipped if the packet is UDP...
     */
    n = libnet_pblock_append(l, p, ((uint8_t *)&dns_hdr) + offset, h_len);
    if (n == -1)
    {
        goto bad;
    }

    /* boilerplate payload sanity check / append macro */
    LIBNET_DO_PAYLOAD(l, p);
 
    return (ptag ? ptag : libnet_pblock_update(l, p, h, LIBNET_PBLOCK_DNSV4_H));
bad:
    libnet_pblock_delete(l, p);
    return (-1);
}
Beispiel #3
0
libnet_ptag_t
libnet_build_gre(uint16_t fv, uint16_t type, uint16_t sum, 
uint16_t offset, uint32_t key, uint32_t seq, uint16_t len,
const uint8_t *payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
{
    uint32_t n;
    libnet_pblock_t *p;
    struct libnet_gre_hdr gre_hdr;

    if (l == NULL)
    { 
        return (-1); 
    }

    n = libnet_getgre_length(fv) + payload_s;

    /*
     *  Find the existing protocol block if a ptag is specified, or create
     *  a new one.
     */
    p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_GRE_H);
    if (p == NULL)
    {
        return (-1);
    }

    gre_hdr.flags_ver = htons(fv);
    gre_hdr.type      = htons(type);
    n = libnet_pblock_append(l, p, (uint8_t *)&gre_hdr, LIBNET_GRE_H);
    if (n == -1)
    {
        /* err msg set in libnet_pblock_append() */
        goto bad; 
    }

    if ((!(fv & GRE_VERSION_MASK) && (fv & (GRE_CSUM|GRE_ROUTING))) || /* v0 */
	(fv & GRE_VERSION_MASK))                                       /* v1 */
    {
        sum = htons(sum);
        n = libnet_pblock_append(l, p, (uint8_t*)&sum,
                sizeof(gre_hdr.gre_sum));
	if (n == -1)
	{
	    /* err msg set in libnet_pblock_append() */
	    goto bad;
	}
	offset = htons(offset);
	n = libnet_pblock_append(l, p, (uint8_t*)&offset, 
                sizeof(gre_hdr.gre_offset));
	if (n == -1)
	{
	    /* err msg set in libnet_pblock_append() */
	    goto bad;
	}
    }

    if ((!(fv & GRE_VERSION_MASK) && (fv & GRE_KEY)) ||                /* v0 */
	( (fv & GRE_VERSION_MASK) && (fv & GRE_SEQ)) )                 /* v1 */
    {
	key = htonl(key);
	n = libnet_pblock_append(l, p, (uint8_t*)&key,
                sizeof(gre_hdr.gre_key));
	if (n == -1)
	{
	    /* err msg set in libnet_pblock_append() */
	    goto bad;
	}
    }

    if ((!(fv & GRE_VERSION_MASK) && (fv & GRE_SEQ)) ||                /* v0 */
	( (fv & GRE_VERSION_MASK) && (fv & GRE_ACK)) )                 /* v1 */
    {
	seq = htonl(seq);
	n = libnet_pblock_append(l, p, (uint8_t*)&seq, 
                sizeof(gre_hdr.gre_seq));
	if (n == -1)
	{
	    /* err msg set in libnet_pblock_append() */
	    goto bad;
	}
    }

    /* boilerplate payload sanity check / append macro */
    LIBNET_DO_PAYLOAD(l, p);

    if ( (fv & GRE_CSUM) && (!sum) )
    {
	libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
    }

    return (ptag ? ptag : libnet_pblock_update(l, p, len, LIBNET_PBLOCK_GRE_H));

bad:
    libnet_pblock_delete(l, p);
    return (-1);
}
libnet_ptag_t
libnet_build_igmp(u_int8_t type, u_int8_t code, u_int16_t sum, u_int32_t ip,
                  u_int8_t *payload, u_int32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
{
    u_int32_t n, h;
    libnet_pblock_t *p;
    struct libnet_igmp_hdr igmp_hdr;

    if (l == NULL)
    {
        return (-1);
    }

    n = LIBNET_IGMP_H + payload_s;
    h = LIBNET_IGMP_H;

    /*
     *  Find the existing protocol block if a ptag is specified, or create
     *  a new one.
     */
    p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_IGMP_H);
    if (p == NULL)
    {
        return (-1);
    }

    memset(&igmp_hdr, 0, sizeof(igmp_hdr));
    igmp_hdr.igmp_type         = type;    /* packet type */
    igmp_hdr.igmp_code         = code;    /* packet code */
    igmp_hdr.igmp_sum          = (sum ? htons(sum) : 0);  /* packet checksum */
    igmp_hdr.igmp_group.s_addr = htonl(ip);

    n = libnet_pblock_append(l, p, (u_int8_t *)&igmp_hdr, LIBNET_IGMP_H);
    if (n == -1)
    {
        goto bad;
    }

    if ((payload && !payload_s) || (!payload && payload_s))
    {
        snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
                 "%s(): payload inconsistency\n", __func__);
        goto bad;
    }

    if (payload && payload_s)
    {
        n = libnet_pblock_append(l, p, payload, payload_s);
        if (n == -1)
        {
            goto bad;
        }
    }
    if (sum == 0)
    {
        /*
         *  If checksum is zero, by default libnet will compute a checksum
         *  for the user.  The programmer can override this by calling
         *  libnet_toggle_checksum(l, ptag, 1);
         */
        libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
    }


    return (ptag ? ptag : libnet_pblock_update(l, p, h, LIBNET_PBLOCK_IGMP_H));
bad:
    libnet_pblock_delete(l, p);
    return (-1);
}
Beispiel #5
0
libnet_ptag_t
libnet_build_cdp(uint8_t version, uint8_t ttl, uint16_t sum, uint16_t type,
uint16_t len, const uint8_t *value, const uint8_t *payload, uint32_t payload_s,
libnet_t *l, libnet_ptag_t ptag)
{
    uint32_t n,h;
    libnet_pblock_t *p;
    struct libnet_cdp_hdr cdp_hdr;

    if (l == NULL)
    { 
        return (-1); 
    }

    n = LIBNET_CDP_H + LIBNET_CDP_H + len + payload_s;
    h = LIBNET_CDP_H + LIBNET_CDP_H + len + payload_s;

    /*
     *  Find the existing protocol block if a ptag is specified, or create
     *  a new one.
     */
    p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_CDP_H);
    if (p == NULL)
    {
        return (-1);
    }

    memset(&cdp_hdr, 0, sizeof(cdp_hdr));
    cdp_hdr.cdp_version = version;
    cdp_hdr.cdp_ttl     = ttl;
    cdp_hdr.cdp_sum     = (sum ? htons(sum) : 0);
    cdp_hdr.cdp_type    = htons(type);
    cdp_hdr.cdp_len     = htons(len + 4);   /* 4 bytes for len and type */

    n = libnet_pblock_append(l, p, (uint8_t *)&cdp_hdr, LIBNET_CDP_H);
    if (n == -1)
    {
        goto bad;
    }

    n = libnet_pblock_append(l, p, value, len);
    if (n == -1)
    {
        /* err msg set in libnet_pblock_append() */
        goto bad;
    }

    /* boilerplate payload sanity check / append macro */
    LIBNET_DO_PAYLOAD(l, p);

    if (sum == 0)
    {
        /*
         *  If checksum is zero, by default libnet will compute a checksum
         *  for the user.  The programmer can override this by calling
         *  libnet_toggle_checksum(l, ptag, 1);
         */
        libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
    }
    return (ptag ? ptag : libnet_pblock_update(l, p, h, LIBNET_PBLOCK_CDP_H));
bad:
    libnet_pblock_delete(l, p);
    return (-1);
}
Beispiel #6
0
libnet_ptag_t
libnet_build_hsrp(uint8_t version, uint8_t opcode, uint8_t state, 
uint8_t hello_time, uint8_t hold_time, uint8_t priority, uint8_t group,
uint8_t reserved, uint8_t authdata[HSRP_AUTHDATA_LENGTH], uint32_t virtual_ip,
const uint8_t *payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
{
    uint32_t n;
    libnet_pblock_t *p;
    struct libnet_hsrp_hdr hsrp_hdr;

    if (l == NULL)
    { 
        return (-1);
    } 

    /*
     *  Find the existing protocol block if a ptag is specified, or create
     *  a new one.
     */
    p = libnet_pblock_probe(l, ptag, LIBNET_HSRP_H + payload_s, LIBNET_PBLOCK_HSRP_H);
    if (p == NULL)
    {
        return (-1);
    }

    memset(&hsrp_hdr, 0, sizeof(hsrp_hdr));
    hsrp_hdr.version = version;
    hsrp_hdr.opcode = opcode;
    hsrp_hdr.state = state;
    hsrp_hdr.hello_time = hello_time;
    hsrp_hdr.hold_time = hold_time;
    hsrp_hdr.priority = priority;
    hsrp_hdr.group = group;
    hsrp_hdr.reserved = reserved;
    memcpy(hsrp_hdr.authdata, authdata, HSRP_AUTHDATA_LENGTH*sizeof(uint8_t));
    hsrp_hdr.virtual_ip = virtual_ip;

    n = libnet_pblock_append(l, p, (uint8_t *)&hsrp_hdr, LIBNET_HSRP_H);
    if (n == -1)
    {
        goto bad;
    }

    if (payload_s && !payload)
    {
        snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
                "%s(): payload inconsistency\n", __func__);
        goto bad;
    }
 
    if (payload_s)
    {
        n = libnet_pblock_append(l, p, payload, payload_s);
        if (n == -1)
        {
            goto bad;
        }
    }
 
    return (ptag ? ptag : libnet_pblock_update(l, p, 0, LIBNET_PBLOCK_HSRP_H));
bad:
    libnet_pblock_delete(l, p);
    return (-1);
}
libnet_ptag_t
libnet_build_vrrp(uint8_t version, uint8_t type, uint8_t vrouter_id, 
uint8_t priority, uint8_t ip_count, uint8_t auth_type, uint8_t advert_int,
uint16_t sum, const uint8_t *payload, uint32_t payload_s, libnet_t *l,
libnet_ptag_t ptag)
{
    uint32_t n, h;
    libnet_pblock_t *p;
    struct libnet_vrrp_hdr vrrp_hdr;

    if (l == NULL)
    { 
        return (-1);
    } 

    n = LIBNET_VRRP_H + payload_s;
    h = LIBNET_VRRP_H + payload_s;

    /*
     *  Find the existing protocol block if a ptag is specified, or create
     *  a new one.
     */
    p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_VRRP_H);
    if (p == NULL)
    {
        return (-1);
    }

    memset(&vrrp_hdr, 0, sizeof(vrrp_hdr));
    vrrp_hdr.vrrp_v          = version;
    vrrp_hdr.vrrp_t          = type;
    vrrp_hdr.vrrp_vrouter_id = vrouter_id;
    vrrp_hdr.vrrp_priority   = priority;
    vrrp_hdr.vrrp_ip_count   = ip_count;
    vrrp_hdr.vrrp_auth_type  = auth_type;
    vrrp_hdr.vrrp_advert_int = advert_int;
    vrrp_hdr.vrrp_sum        = (sum ? htons(sum) : 0);

    n = libnet_pblock_append(l, p, (uint8_t *)&vrrp_hdr, LIBNET_VRRP_H);
    if (n == -1)
    {
        goto bad;
    }

    /* boilerplate payload sanity check / append macro */
    LIBNET_DO_PAYLOAD(l, p);

    if (sum == 0)
    {
        /*
         *  If checksum is zero, by default libnet will compute a checksum
         *  for the user.  The programmer can override this by calling
         *  libnet_toggle_checksum(l, ptag, 1);
         */
        libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
    }
    return (ptag ? ptag : libnet_pblock_update(l, p, h, LIBNET_PBLOCK_VRRP_H));
bad:
    libnet_pblock_delete(l, p);
    return (-1);
}
Beispiel #8
0
libnet_ptag_t
libnet_build_dhcpv4(uint8_t opcode, uint8_t htype, uint8_t hlen, 
uint8_t hopcount, uint32_t xid, uint16_t secs, uint16_t flags,
uint32_t cip, uint32_t yip, uint32_t sip, uint32_t gip, const uint8_t *chaddr,
const char *sname, const char *file, const uint8_t *payload, uint32_t payload_s,
libnet_t *l, libnet_ptag_t ptag)
{
    uint32_t n, h;
    libnet_pblock_t *p; 
    struct libnet_dhcpv4_hdr dhcp_hdr;

    if (l == NULL)
    { 
        return (-1);
    } 

    n = LIBNET_DHCPV4_H + payload_s;
    h = 0;          /* no checksum */
 
    /*
     *  Find the existing protocol block if a ptag is specified, or create
     *  a new one.
     */
    p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_DHCPV4_H);
    if (p == NULL)
    {
        return (-1);
    }

    memset(&dhcp_hdr, 0, sizeof(dhcp_hdr));
    dhcp_hdr.dhcp_opcode    = opcode;
    dhcp_hdr.dhcp_htype     = htype;
    dhcp_hdr.dhcp_hlen      = hlen;
    dhcp_hdr.dhcp_hopcount  = hopcount;
    dhcp_hdr.dhcp_xid       = htonl(xid);
    dhcp_hdr.dhcp_secs      = htons(secs);
    dhcp_hdr.dhcp_flags     = htons(flags);
    dhcp_hdr.dhcp_cip       = htonl(cip);
    dhcp_hdr.dhcp_yip       = htonl(yip);
    dhcp_hdr.dhcp_sip       = htonl(sip);
    dhcp_hdr.dhcp_gip       = htonl(gip);

    if (chaddr)
    {
        size_t n = sizeof (dhcp_hdr.dhcp_chaddr);
        if (hlen < n)
            n = hlen;
        memcpy(dhcp_hdr.dhcp_chaddr, chaddr, n);
    }
    if (sname)
    { 
        strncpy(dhcp_hdr.dhcp_sname, sname, sizeof (dhcp_hdr.dhcp_sname) - 1);
    }
    if (file)
    {
        strncpy(dhcp_hdr.dhcp_file, file, sizeof (dhcp_hdr.dhcp_file) - 1);
    }
    dhcp_hdr.dhcp_magic = htonl(DHCP_MAGIC);

    n = libnet_pblock_append(l, p, (uint8_t *)&dhcp_hdr, LIBNET_DHCPV4_H);
    if (n == -1)
    {
        goto bad;
    }

    if (payload_s && !payload)
    {
         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
                 "%s(): payload inconsistency\n", __func__);
        goto bad;
    }
 
    if (payload_s)
    {
        n = libnet_pblock_append(l, p, payload, payload_s);
        if (n == -1)
        {
            goto bad;
        }
    }
 
    return (ptag ? ptag : libnet_pblock_update(l, p, h, 
            LIBNET_PBLOCK_DHCPV4_H));
bad:
    libnet_pblock_delete(l, p);
    return (-1);
}
Beispiel #9
0
libnet_ptag_t
libnet_build_ospfv2_lsa(uint16_t age, uint8_t opts, uint8_t type, uint32_t lsid,
uint32_t advrtr, uint32_t seqnum, uint16_t sum, uint16_t len,
const uint8_t *payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
{
    uint32_t n, h;
    libnet_pblock_t *p;
    struct libnet_lsa_hdr lsa_hdr;

    if (l == NULL)
    { 
        return (-1);
    } 

    n = LIBNET_OSPF_LSA_H + payload_s;
    h = len + payload_s;

    /*
     *  Find the existing protocol block if a ptag is specified, or create
     *  a new one.
     */
    p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_OSPF_LSA_H);
    if (p == NULL)
    {
        return (-1);
    }

    memset(&lsa_hdr, 0, sizeof(lsa_hdr));
    lsa_hdr.lsa_age         = htons(age);
    lsa_hdr.lsa_opts        = opts;
    lsa_hdr.lsa_type        = type;
    lsa_hdr.lsa_id          = htonl(lsid);
    lsa_hdr.lsa_adv.s_addr  = htonl(advrtr);
    lsa_hdr.lsa_seq         = htonl(seqnum);
    lsa_hdr.lsa_sum         = sum;
    lsa_hdr.lsa_len         = htons(h);

    n = libnet_pblock_append(l, p, (uint8_t *)&lsa_hdr, LIBNET_OSPF_LSA_H);
    if (n == -1)
    {
        goto bad;
    }

    /* boilerplate payload sanity check / append macro */
    LIBNET_DO_PAYLOAD(l, p);

    if (sum == 0)
    {
        /*
         *  If checksum is zero, by default libnet will compute a checksum
         *  for the user.  The programmer can override this by calling
         *  libnet_toggle_checksum(l, ptag, 1);
         */
        libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
    }
    return (ptag ? ptag : libnet_pblock_update(l, p, h, 
            LIBNET_PBLOCK_OSPF_LSA_H));
bad:
    libnet_pblock_delete(l, p);
    return (-1);
}
Beispiel #10
0
libnet_ptag_t
libnet_build_802_1q(u_char *dst, u_char *src, u_short tpi, u_char priority,
            u_char cfi, u_short vid, u_short len, u_char *payload,
            u_long payload_s, libnet_t *l, libnet_ptag_t ptag)
{
    u_long n;
    u_short h;
    libnet_pblock_t *p;
    struct libnet_802_1q_hdr _802_1q_hdr;

    if (l == NULL)
    { 
        return (-1);
    } 

    n = LIBNET_802_1Q_H + payload_s;
    h = 0;
 
    /*
     *  Find the existing protocol block if a ptag is specified, or create
     *  a new one.
     */
    p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_802_1Q_H);
    if (p == NULL)
    {
        return (-1);
    }

    memcpy(_802_1q_hdr.vlan_dhost, dst, ETHER_ADDR_LEN);
    memcpy(_802_1q_hdr.vlan_shost, src, ETHER_ADDR_LEN);
    _802_1q_hdr.vlan_tpi = htons(tpi);
    _802_1q_hdr.vlan_priority_c_vid = htons((priority << 13) | (cfi << 12)
            | (vid & LIBNET_802_1Q_VIDMASK));
    _802_1q_hdr.vlan_len = htons(len);

    n = libnet_pblock_append(l, p, (u_char *)&_802_1q_hdr, LIBNET_802_1Q_H);
    if (n == -1)
    {
        goto bad;
    }
 
    if (payload && payload_s)
    {
        n = libnet_pblock_append(l, p, payload, payload_s);
        if (n == -1)
        {
            goto bad;
        }
    }
 
    /*
     *  The link offset is actually 18 bytes (the VLAN tag is 4 bytes).  We
     *  need to update the link offset pointer...  XXX - should we set this
     *  here..?  Probably not.  We should make pblock_coalesce check the
     *  pblock type and adjust accordingly.
     */
    l->link_offset = 0x12;
    return (ptag ? ptag : libnet_pblock_update(l, p, h,
            LIBNET_PBLOCK_802_1Q_H));
bad:
    libnet_pblock_free(p);
    return (-1);
}