Exemple #1
0
static void rtp_proto_info_ctor(struct rtp_proto_info *info, struct parser *parser, struct proto_info *parent, struct rtp_hdr const *rtph, size_t head_len, size_t payload)
{
    proto_info_ctor(&info->info, parser, parent, head_len, payload);
    info->payload_type = READ_U8(&rtph->flags1) & F1_PLD_TYPE_MASK;
    info->sync_src = READ_U32N(&rtph->ssrc);
    info->seq_num = READ_U16N(&rtph->seq_num);
    info->timestamp = READ_U32N(&rtph->timestamp);
}
Exemple #2
0
static void tcp_proto_info_ctor(struct tcp_proto_info *info, struct parser *parser, struct proto_info *parent, size_t head_len, size_t payload, uint16_t sport, uint16_t dport, struct tcp_hdr const *tcphdr)
{
    proto_info_ctor(&info->info, parser, parent, head_len, payload);

    info->key.port[0] = sport;
    info->key.port[1] = dport;
    uint8_t const flags = READ_U8(&tcphdr->flags);
    info->syn = !!(flags & TCP_SYN_MASK);
    info->ack = !!(flags & TCP_ACK_MASK);
    info->rst = !!(flags & TCP_RST_MASK);
    info->fin = !!(flags & TCP_FIN_MASK);
    info->urg = !!(flags & TCP_URG_MASK);
    info->psh = !!(flags & TCP_PSH_MASK);
    // to_srv set later from tcp_subparser
    info->window = READ_U16N(&tcphdr->window);
    info->urg_ptr = READ_U16N(&tcphdr->urg_ptr);
    info->ack_num = READ_U32N(&tcphdr->ack_seq);
    info->seq_num = READ_U32N(&tcphdr->seq_num);
    info->rel_seq_num = 0U;
    info->set_values = 0;   // options will be set later
    info->nb_options = 0;
}
Exemple #3
0
static enum proto_parse_status netbios_parse_frame(struct netbios_parser *netbios_parser,
        struct proto_info *parent, unsigned way, uint8_t const *packet, size_t cap_len, size_t wire_len,
        struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet, size_t *pos)
{
    if (cap_len == 0 && netbios_parser->sbuf.dir[way].cap_len == 0) {
        // Ignore pure gap start
        timeval_reset(netbios_parser->first_packet_tv + way);
        return PROTO_PARSE_ERR;
    }
    if (cap_len > 0 && !timeval_is_set(&netbios_parser->first_packet_tv[way])) {
        SLOG(LOG_DEBUG, "Set first packet ts for way %d to %s", way, timeval_2_str(now));
        netbios_parser->first_packet_tv[way] = *now;
    }
    if (wire_len < NETBIOS_HEADER_SIZE + SMB_FLAG_SIZE) {
        streambuf_set_restart(&netbios_parser->sbuf, way, packet, NETBIOS_HEADER_SIZE + SMB_FLAG_SIZE);
        return PROTO_OK;
    }
    if (cap_len < NETBIOS_HEADER_SIZE + SMB_FLAG_SIZE) {
        SLOG(LOG_DEBUG, "Got a gap on neccessary bytes");
        timeval_reset(netbios_parser->first_packet_tv + way);
        return PROTO_PARSE_ERR;
    }
    if (packet[0] != 0) {
        SLOG(LOG_DEBUG, "Expected Session message type 0x00, got 0x%"PRIx8, packet[0]);
        timeval_reset(netbios_parser->first_packet_tv + way);
        return PROTO_PARSE_ERR;
    }

    uint32_t smb_version = READ_U32N(packet + NETBIOS_HEADER_SIZE);
    if (smb_version != CIFS_SMB_HEADER && smb_version != CIFS_SMB2_HEADER) {
        static unsigned char smb_header[SMB_FLAG_SIZE] = {0xff, 0x53, 0x4d, 0x42};
        static unsigned char smb2_header[SMB_FLAG_SIZE] = {0xfe, 0x53, 0x4d, 0x42};
        void *res = memmem(packet + NETBIOS_HEADER_SIZE, cap_len - NETBIOS_HEADER_SIZE, &smb_header,
                SMB_FLAG_SIZE);
        if (!res) {
            res = memmem(packet + NETBIOS_HEADER_SIZE, cap_len - NETBIOS_HEADER_SIZE, &smb2_header,
                    SMB_FLAG_SIZE);
        }
        if (!res) {
            SLOG(LOG_DEBUG, "Netbios payload does not expected header (expected %"PRIx32" or %"PRIx32"),"
                    " got %"PRIx32, CIFS_SMB_HEADER, CIFS_SMB2_HEADER, smb_version);
            return PROTO_PARSE_ERR;
        }
        SLOG(LOG_DEBUG, "Found a SMB header in payload, restarting there");
        timeval_reset(netbios_parser->first_packet_tv + way);
        streambuf_set_restart(&netbios_parser->sbuf, way, res - NETBIOS_HEADER_SIZE,
                NETBIOS_HEADER_SIZE + SMB_FLAG_SIZE);
        return PROTO_OK;
    }

    uint32_t len = READ_U32N((uint32_t*) packet) & 0x00ffffff;
    *pos = len;
    size_t current_payload = wire_len - NETBIOS_HEADER_SIZE;
    SLOG(LOG_DEBUG, "Found netbios payload of %"PRIu32", current payload %zu",
            len, current_payload);
    if (len > current_payload) {
        streambuf_set_restart(&netbios_parser->sbuf, way, packet, len + NETBIOS_HEADER_SIZE);
        return PROTO_OK;
    }

    /* Parse */
    struct netbios_proto_info info;
    netbios_proto_info_ctor(&info, &netbios_parser->parser, parent,
            NETBIOS_HEADER_SIZE, wire_len - NETBIOS_HEADER_SIZE, len,
            netbios_parser->first_packet_tv + way);
    timeval_reset(netbios_parser->first_packet_tv + way);

    SLOG(LOG_DEBUG, "Parsing netbios content");
    uint8_t const *next_packet = packet + NETBIOS_HEADER_SIZE;
    if (!netbios_parser->msg_parser) {
        netbios_parser->msg_parser = proto_cifs->ops->parser_new(proto_cifs);
    }

    enum proto_parse_status status = PROTO_OK;
    if (netbios_parser->msg_parser) {
        status = proto_parse(netbios_parser->msg_parser, &info.info,
                way, next_packet,
                cap_len - NETBIOS_HEADER_SIZE, wire_len - NETBIOS_HEADER_SIZE,
                now, tot_cap_len, tot_packet);
        if (status == PROTO_OK) return PROTO_OK;
    }
    (void)proto_parse(NULL, &info.info, way, next_packet, cap_len - NETBIOS_HEADER_SIZE, wire_len - NETBIOS_HEADER_SIZE, now, tot_cap_len, tot_packet);
    return status;
}
Exemple #4
0
static enum proto_parse_status tcp_parse(struct parser *parser, struct proto_info *parent, unsigned way, uint8_t const *packet, size_t cap_len, size_t wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet)
{
    struct mux_parser *mux_parser = DOWNCAST(parser, parser, mux_parser);
    struct tcp_hdr const *tcphdr = (struct tcp_hdr *)packet;

    // Sanity checks
    if (wire_len < sizeof(*tcphdr)) {
        SLOG(LOG_DEBUG, "Bogus TCP packet: too short (%zu < %zu)", wire_len, sizeof(*tcphdr));
        return PROTO_PARSE_ERR;
    }

    if (cap_len < sizeof(*tcphdr)) return PROTO_TOO_SHORT;

    size_t tcphdr_len = TCP_HDR_LENGTH(tcphdr);

    if (tcphdr_len < sizeof(*tcphdr)) {
        SLOG(LOG_DEBUG, "Bogus TCP packet: header size too smal (%zu < %zu)", tcphdr_len, sizeof(*tcphdr));
        return PROTO_PARSE_ERR;
    }

    if (tcphdr_len > wire_len) {
        SLOG(LOG_DEBUG, "Bogus TCP packet: wrong length %zu > %zu", tcphdr_len, wire_len);
        return PROTO_PARSE_ERR;
    }

    if (tcphdr_len > cap_len) return PROTO_TOO_SHORT;

    // TODO: move this below call to tcp_proto_info_ctor() and use info instead of reading tcphdr directly
    uint16_t const sport = READ_U16N(&tcphdr->src);
    uint16_t const dport = READ_U16N(&tcphdr->dst);
    bool const syn = !!(READ_U8(&tcphdr->flags) & TCP_SYN_MASK);
    bool const fin = !!(READ_U8(&tcphdr->flags) & TCP_FIN_MASK);
    bool const ack = !!(READ_U8(&tcphdr->flags) & TCP_ACK_MASK);
    bool const rst = !!(READ_U8(&tcphdr->flags) & TCP_RST_MASK);
    bool const urg = !!(READ_U8(&tcphdr->flags) & TCP_URG_MASK);
    bool const psh = !!(READ_U8(&tcphdr->flags) & TCP_PSH_MASK);
    SLOG(LOG_DEBUG, "New TCP packet of %zu bytes (%zu captured), %zu payload, ports %"PRIu16" -> %"PRIu16" Flags: %s%s%s%s%s%s, Seq:%"PRIu32", Ack:%"PRIu32,
        wire_len, cap_len, wire_len - tcphdr_len, sport, dport,
        syn ? "Syn":"", fin ? "Fin":"", ack ? "Ack":"", rst ? "Rst":"", urg ? "Urg":"", psh ? "Psh":"",
        READ_U32N(&tcphdr->seq_num), READ_U32N(&tcphdr->ack_seq));

    // Parse

    struct tcp_proto_info info;
    tcp_proto_info_ctor(&info, parser, parent, tcphdr_len, wire_len - tcphdr_len, sport, dport, tcphdr);

    // Parse TCP options
    uint8_t const *options = (uint8_t *)(tcphdr+1);
    assert(tcphdr_len >= sizeof(*tcphdr));
    for (size_t rem_len = tcphdr_len - sizeof(*tcphdr); rem_len > 0; ) {
        ssize_t const len = parse_next_option(&info, options, rem_len);
        if (len < 0) return PROTO_PARSE_ERR;
        rem_len -= len;
        options += len;
    }

    // Search an already spawned subparser
    struct port_key key;
    port_key_init(&key, sport, dport, way);
    struct mux_subparser *subparser = mux_subparser_lookup(mux_parser, NULL, NULL, &key, now);
    if (subparser) SLOG(LOG_DEBUG, "Found subparser@%p for this cnx, for proto %s", subparser->parser, subparser->parser->proto->name);

    if (! subparser) {
        struct proto *requestor = NULL;
        struct proto *sub_proto = NULL;
        // Use connection tracking first
        ASSIGN_INFO_OPT2(ip, ip6, parent);
        if (! ip) ip = ip6;
        if (ip) sub_proto = cnxtrack_ip_lookup(IPPROTO_TCP, ip->key.addr+0, sport, ip->key.addr+1, dport, now, &requestor);
        if (! sub_proto) { // Then try predefined ports
            sub_proto = port_muxer_find(&tcp_port_muxers, info.key.port[0], info.key.port[1]);
        }
        if (sub_proto) {
            subparser = mux_subparser_and_parser_new(mux_parser, sub_proto, requestor, &key, now);
        } else {
            // Even if we have no child parser to send payload to, we want to submit payload in stream order to our plugins
            subparser = tcp_subparser_new(mux_parser, NULL, NULL, &key, now);
        }
    }

    if (! subparser) goto fallback;

    // Keep track of TCP flags & ISN
    struct tcp_subparser *tcp_sub = DOWNCAST(subparser, mux_subparser, tcp_subparser);
    mutex_lock(tcp_sub->mutex);
    if (
        info.ack &&
        (!IS_SET_FOR_WAY(way, tcp_sub->ack) || seqnum_gt(info.ack_num, tcp_sub->max_acknum[way]))
    ) {
        SET_FOR_WAY(way, tcp_sub->ack);
        tcp_sub->max_acknum[way] = info.ack_num;
    }
    if (info.fin) {
        SET_FOR_WAY(way, tcp_sub->fin);
        tcp_sub->fin_seqnum[way] = info.seq_num + info.info.payload;    // The FIN is acked after the payload
    }
    if (info.syn && !IS_SET_FOR_WAY(way, tcp_sub->syn)) {
        SET_FOR_WAY(way, tcp_sub->syn);
        tcp_sub->isn[way] = info.seq_num;
    }
    if (!IS_SET_FOR_WAY(way, tcp_sub->origin)) {
        SET_FOR_WAY(way, tcp_sub->origin);
        tcp_sub->wl_origin[way] = info.seq_num;
        if (! IS_SET_FOR_WAY(way, tcp_sub->syn)) SLOG(LOG_DEBUG, "Starting a WL while SYN is yet to be received!");
    }

    // Set relative sequence number if we know it
    if (IS_SET_FOR_WAY(way, tcp_sub->syn)) info.rel_seq_num = info.seq_num - tcp_sub->isn[way];

    // Set srv_way
    assert(tcp_sub->srv_set < 3);
    if (tcp_sub->srv_set == 0 || (tcp_sub->srv_set == 1 && info.syn)) {
        if (comes_from_client(info.key.port, info.syn, info.ack)) {
            // this packet comes from the client
            tcp_sub->srv_way = !way;
        } else {
            tcp_sub->srv_way = way;
        }
        tcp_sub->srv_set = info.syn ? 2:1;
    }
    // Now patch it into tcp info
    info.to_srv = tcp_sub->srv_way != way;

    SLOG(LOG_DEBUG, "Subparser@%p state: >ISN:%"PRIu32"%s Fin:%"PRIu32" Ack:%"PRIu32" <ISN:%"PRIu32"%s Fin:%"PRIu32" Ack:%"PRIu32", SrvWay=%u%s",
        subparser->parser,
        IS_SET_FOR_WAY(0, tcp_sub->syn) ? tcp_sub->isn[0] : IS_SET_FOR_WAY(0, tcp_sub->origin) ? tcp_sub->wl_origin[0] : 0,
        IS_SET_FOR_WAY(0, tcp_sub->syn) ? "" : " (approx)",
        IS_SET_FOR_WAY(0, tcp_sub->fin) ? tcp_sub->fin_seqnum[0] : 0,
        IS_SET_FOR_WAY(0, tcp_sub->ack) ? tcp_sub->max_acknum[0] : 0,
        IS_SET_FOR_WAY(1, tcp_sub->syn) ? tcp_sub->isn[1] : IS_SET_FOR_WAY(1, tcp_sub->origin) ? tcp_sub->wl_origin[1] : 0,
        IS_SET_FOR_WAY(1, tcp_sub->syn) ? "" : " (approx)",
        IS_SET_FOR_WAY(1, tcp_sub->fin) ? tcp_sub->fin_seqnum[1] : 0,
        IS_SET_FOR_WAY(1, tcp_sub->ack) ? tcp_sub->max_acknum[1] : 0,
        tcp_sub->srv_way,
        tcp_sub->srv_set == 0 ? " (unset)":
            tcp_sub->srv_set == 1 ? " (unsure)":"(certain)");

    enum proto_parse_status err;
    /* Use the wait_list to parse this packet.
       Notice that we do queue empty packets because subparser (or subscriber) want to receive all packets in order, including empty ones. */

    size_t const packet_len = wire_len - tcphdr_len;
    assert(IS_SET_FOR_WAY(way, tcp_sub->origin));
    unsigned const offset = info.seq_num - tcp_sub->wl_origin[way];
    unsigned const next_offset = offset + packet_len + info.syn + info.fin;
    unsigned const sync_offset = info.ack_num - tcp_sub->wl_origin[!way];  // we must not parse this one before we parsed (or timeouted) this one from wl[!way]
    // FIXME: Here the parser is chosen before we actually parse anything. If later the parser fails we cannot try another one.
    //        Choice of parser should be delayed until we start actual parse.
    bool const do_sync = info.ack && IS_SET_FOR_WAY(!way, tcp_sub->origin);
    err = pkt_wait_list_add(tcp_sub->wl+way, offset, next_offset, do_sync, sync_offset, true, &info.info, way, packet + tcphdr_len, cap_len - tcphdr_len, packet_len, now, tot_cap_len, tot_packet);
    SLOG(LOG_DEBUG, "Waiting list returned %s", proto_parse_status_2_str(err));

    if (err == PROTO_OK) {
        // Try advancing each WL until we are stuck or met an error
        pkt_wait_list_try_both(tcp_sub->wl+!way, &err, now, false);
    }

    bool const term = tcp_subparser_term(tcp_sub);
    mutex_unlock(tcp_sub->mutex);

    if (term || err == PROTO_PARSE_ERR) {
        if (term) {
            SLOG(LOG_DEBUG, "TCP cnx terminated (was %s)", parser_name(subparser->parser));
        } else {
            SLOG(LOG_DEBUG, "No suitable subparser for this payload");
        }
        mux_subparser_deindex(subparser);
    }
    mux_subparser_unref(&subparser);

    if (err == PROTO_OK) return PROTO_OK;

fallback:
    (void)proto_parse(NULL, &info.info, way, packet + tcphdr_len, cap_len - tcphdr_len, wire_len - tcphdr_len, now, tot_cap_len, tot_packet);
    return PROTO_OK;
}
Exemple #5
0
static enum proto_parse_status dhcp_parse(struct parser *parser, struct proto_info *parent, unsigned way, uint8_t const *payload, size_t cap_len, size_t wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet)
{
    struct dhcp const *dhcp = (struct dhcp *)payload;

    // Sanity Checks

    // Check that we have at least the size of an DHCP packet for IP protocol
    if (wire_len < sizeof(*dhcp)) return PROTO_PARSE_ERR;
    // And that we have enough data to parse it
    if (cap_len < sizeof(*dhcp)) return PROTO_TOO_SHORT;

    if (0 != memcmp(dhcp->cookie, &magic_cookie, sizeof(magic_cookie))) {
        SLOG(LOG_DEBUG, "Bad magic Cookie");
        return PROTO_PARSE_ERR;
    }

    struct dhcp_proto_info info;
    proto_info_ctor(&info.info, parser, parent, wire_len, 0);
    info.opcode = READ_U8(&dhcp->op);
    if (info.opcode != BOOTP_REQUEST && info.opcode != BOOTP_REPLY) {
        SLOG(LOG_DEBUG, "Unknown DHCP opcode (%u)", info.opcode);
        return PROTO_PARSE_ERR;
    }
    uint8_t const hlen = READ_U8(&dhcp->hlen);
    if (hlen > sizeof(dhcp->chaddr)) {
        SLOG(LOG_DEBUG, "Bad hlen in DHCP (%u)", hlen);
        return PROTO_PARSE_ERR;
    }
    info.xid = READ_U32N(&dhcp->xid);
    info.set_values = 0;
    uint32_t const addr = READ_U32(&dhcp->yiaddr);
    if (addr) {
        info.set_values |= DHCP_CLIENT_SET;
        ip_addr_ctor_from_ip4(&info.client, addr);
    }
    uint8_t const htype = READ_U8(&dhcp->htype);
    info.hw_addr_is_eth = htype == 1;
    if (info.hw_addr_is_eth) {
        if (hlen != sizeof(info.client_mac)) {
            SLOG(LOG_DEBUG, "Bad hlen (%u) for Eth type", hlen);
            return PROTO_PARSE_ERR;
        }
        memcpy(info.client_mac, dhcp->chaddr, sizeof(info.client_mac));
    } else {
        memset(info.client_mac, 0, sizeof(info.client_mac));
    }

    memcpy(info.server_name, dhcp->sname, sizeof(info.server_name));

    SLOG(LOG_DEBUG, "New DHCP %s", dhcp_opcode_2_str(info.opcode));

    // parse options
    info.msg_type = 0;  // mandatory
    struct cursor c;
    cursor_ctor(&c, dhcp->options, cap_len - offsetof(struct dhcp, options));
    while (c.cap_len >= 2) {
        uint8_t const type = cursor_read_u8(&c);
        uint8_t const len  = cursor_read_u8(&c);
        if (c.cap_len < len) {
            SLOG(LOG_DEBUG, "Cannot read options");
            return PROTO_PARSE_ERR;
        }
        switch (type) {
            case 53:    // msg type
                if (len != 1) {
                    SLOG(LOG_DEBUG, "Bad length (%"PRIu8") for msg type DHCP option", len);
                    return PROTO_PARSE_ERR;
                }
                info.msg_type = cursor_read_u8(&c);
                if (info.msg_type > DHCP_INFORM) {
                    SLOG(LOG_DEBUG, "Bad DHCP msg type (%u)", info.msg_type);
                    return PROTO_PARSE_ERR;
                }
                break;
            default:
                cursor_drop(&c, len);
                break;
        }
    }
    if (0 == info.msg_type) {   // not found
        SLOG(LOG_DEBUG, "DHCP msg without msg type");
        return PROTO_PARSE_ERR;
    }

    return proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet);
}
Exemple #6
0
static int packet_is_cifs(struct cifs_hdr const *cifshdr)
{
    return READ_U32N(&cifshdr->code) == 0xff534d42; // 0xff + SMB
}